第18节课 【列表编程练习题】
练习01 计算数字的出现次数
题目描述
读取1到100之间的整数,然后计算每个数出现的次数
输入输出描述
输入两行,第一行为整数的个数n,第二行为n个整数
输出多行,每行表示某数及其出现的次数,顺序按照数字从小到大
示例
输入:
9
2 5 6 5 4 3 23 43 2
输出:
2出现2次
3出现1次
4出现1次
5出现2次
6出现1次
23出现1次
43出现1次
# 思路1 借助Python自带功能去做
# def sovle(arr, n):
# arr.sort() #O(n*logn)
# for index in range(len(arr)):
# num = arr[index]
# # 对于第一个数字特殊处理
# # 与前一个数字不相等 第一次出现
# if index == 0 or num != arr[index - 1]:
# print(f"{num}出现{arr.count(num)}次")
# 思路2 排序后 连续做比较
"""
num = 5
count = 2
i
1 1 1 2 2 3 3 3 4 5 5
j
1=>3
2=>2
3=>3
4=>1
5=>1
"""
# def sovle(arr, n):
# #O(nlogn)
# arr.sort()
# i = 0
# # O(n)
# while i < n:
# num = arr[i]
# count = 1
# j = i + 1
# while j < n and arr[j] == arr[i]:
# count += 1
# j += 1
# print(f"{num}出现{count}次")
# print(f"j = {j}")
# i = j
# # 因为此题中需要用到 j = len(arr) 所以不能使用range()
# 思路3 计数排序 非比较类排序 只针对整数 O(n + m)
# m 为最值之间的距离
# 牺牲空间 换 时间
def sovle(arr, n):
# 找最大值和最小值
min_value = min(arr) # O(n)
max_value = max(arr) # O(n)
# 创建计数数组
counts = [0] * (max_value - min_value + 1)
# 统计每个数字出现的次数
for num in arr: # O(n)
index = num - min_value
counts[index] += 1
# 遍历计数数组counts 打印每个数字出现的次数
for index in range(len(counts)): #O(m)
if counts[index] != 0:
num = index + min_value
print(f"{num}出现{counts[index]}")
# 追加问题:如何排序好的数据打印出来?1 1 1 2 2 3 3 .......
# 搞定输入问题
n = int(input())
arr = list(map(int, input().split(" ")))
sovle(arr, n)
练习02 最大公约数 II
题目描述
输入n个数字,求该n个数字的最大公约数
输入输出描述
输入n个数字
输出最大公约数
示例
输入:
9 12 18 21 15
输出:
3
def gcd(arr):
min_value = min(arr)
for i in range(min_value, 0, -1):
for num in arr:
if num % i != 0:
break
else:
return i
arr = list(map(int, input().split(" ")))
print(gcd(arr))
练习03 按奇偶排序数组
题目描述
给你一个整数数组 nums,将 nums 中的的所有偶数元素移动到数组的前面,后跟所有奇数元素。
返回满足此条件的 任一数组 作为答案。
输入输出描述
输入数组长度n,接下来输入n个整数
输出排序后的数组
示例
输入:
4
3 1 2 4
输出:
2 4 3 1
解释:
[4,2,3,1]、[2,4,1,3] 和 [4,2,1,3] 也会被视作正确答案
# 思路1 创建新的数组 分开存奇偶 然后合并
"""
1 2 3 4 5 6 7 8
arr1 = 2 4 6 8
arr2 = 1 3 5 7
arr1 + arr2
"""
# 时间O(n) 空间S(n)
# def sovle(arr:list[int], n:int) -> list[int]:
# arr1 = []
# arr2 = []
# for num in arr:
# if num % 2 == 0:
# arr1.append(num)
# else:
# arr2.append(num)
# return arr1 + arr2
# 思路2 双端队列 表头存偶 表尾存奇
# O(n) S(n)
# def sovle(arr:list[int], n:int) -> list[int]:
# deque = []
# for num in arr:
# if num % 2 == 0:
# deque.insert(0, num)
# else:
# deque.append(num)
# return deque
# 思路3 选择排序
# O(n^2) S(1)
# def sovle(arr:list[int], n:int) -> list[int]:
# for i in range(len(arr) - 1):
# for j in range(i + 1, len(arr)):
# if arr[i] % 2 == 1 and arr[j] % 2 == 0:
# arr[i], arr[j] = arr[j], arr[i]
# break
# return arr
# 思路4 双指针
"""
是一种解题的思路 主要针对的是一维数据 数组/链表
同向指针 有两个角标都是从左到右的遍历
对向指针 有两个角标一个从头到尾遍历 另一个从尾到头遍历
对数组进行分区间
数组有序
快慢指针 同向的 一个走一步 另一走两步
滑动窗口 同向的 关于区间的问题
"""
# O(n) S(1)
# def sovle(arr:list[int], n:int) -> list[int]:
# left = 0
# right = len(arr) - 1
# while left < right:
# # 左偶右奇
# if arr[left] % 2 == 0 and arr[right] % 2 == 1:
# left += 1
# right -= 1
# # 左偶右偶
# elif arr[left] % 2 == 0 and arr[right] % 2 == 0:
# left += 1
# # 左奇右奇
# elif arr[left] % 2 == 1 and arr[right] % 2 == 1:
# right -= 1
# # 左奇右偶
# else:
# arr[left], arr[right] = arr[right], arr[left]
# return arr
# *思路5 保证元素之间的相对顺序 *排序算法的稳定性*
"""
稳定性
现有一组无序数据 ....4(1).....4(2)....4(3).....
排序后 必须保证 ......4(1)4(2)4(3).....
"""
def sovle(arr:list[int], n:int) -> list[int]:
# 冒泡 O(n^2)
# for i in range(len(arr) - 1):
# for j in range(len(arr) - 1 - i):
# if arr[j] % 2 == 1 and arr[j + 1] % 2 == 0:
# arr[j], arr[j + 1] = arr[j + 1], arr[j]
# 插入 O(n^2)
for i in range(1, len(arr)):
if arr[i] % 2 == 1:
continue
j = i
while j > 0 and arr[j - 1] % 2 == 1:
arr[j], arr[j - 1] = arr[j - 1], arr[j]
j -= 1
return arr
n = int(input())
arr = list(map(int, input().split()))
print(sovle(arr, n))
练习04 合并两个有序数组
题目描述
给定两个有序递增的数组A和数组B,将其进行合并成一个新的数组C,且保持有序递增,并输出数组C
输入输出描述
第一行输入数组A的长度n,第二行输入n个元素,第三行输入数组B的长度m,第四行输入m个元素
输出数组C的n+m个元素
示例
输入:
5
1 5 16 61 111
4
2 4 5 6
输出:
1 2 4 5 5 6 16 61 111
# 思路1 合并后排序 O(nlogn) S(n)
# def solve(arr1, arr2):
# arr3 = []
# arr3.extend(arr1)
# arr3.extend(arr2)
# arr3.sort()
# return arr3
# 思路2 按照从小到大 交叉合并 O(n) S(n)
# def solve(arr1, arr2):
# length1 = len(arr1)
# length2 = len(arr2)
# arr3 = [0] * (length1 + length2)
# p1 = 0
# p2 = 0
# p3 = 0
# while p3 < len(arr3):
# if p1 < length1 and p2 >= length2:
# arr3[p3] = arr1[p1]
# p1 += 1
# elif p2 < length2 and p1 >= length1:
# arr3[p3] = arr2[p2]
# p2 += 1
# elif arr1[p1] <= arr2[p2]:
# arr3[p3] = arr1[p1]
# p1 += 1
# else:
# arr3[p3] = arr2[p2]
# p2 += 1
# p3 += 1
# return arr3
# 思路3 同一个数组有两个子区间各自有序 问合并后的结果(原地排序)?
# 归并排序的核心思路
# L-M-R含义是对数组中某一个区间进行合并
def solve(arr, L, M, R):
temp = []
for i in range(L, R + 1):
temp.append(arr[i])
p1 = 0
p2 = M + 1 - L
p3 = L
while p3 <= R:
if p1 <= M - L and p2 > R - L:
arr[p3] = temp[p1]
p1 += 1
elif p1 > M - L and p2 <= R - L:
arr[p3] = temp[p2]
p2 += 1
elif temp[p1] <= temp[p2]:
arr[p3] = temp[p1]
p1 += 1
else:
arr[p3] = temp[p2]
p2 += 1
p3 += 1
"""
1 2 3 4 2 3 6 8
"""
# n = int(input())
# arr1 = list(map(int, input().split(" ")))
# m = int(input())
# arr2 = list(map(int, input().split(" ")))
# arr3 = solve(arr1, arr2)
# print(arr3)
# 思路3
arr = [1,2,3,4,2,3,4,5,6,7,8,9,2,3,6,8]
solve(arr, 8, 11, 15)
print(arr)
练习05 数组划分
题目描述
给定一个数组A,将第一个元素 A 0 A_0 A0作为枢纽,并把数组划分成三个区间,第一个区间所有元素 < A 0 <A_0 <A0,第二个区间所有元素 = = A 0 ==A_0 ==A0,第三个区间所有元素 > A 0 >A_0 >A0
例如数组[5,2,9,3,6,8],划分后的结果为[3,2,5,9,6,8],第一个区间[3,2],第二个区间[5],第三个区间[9,6,8]
结果不唯一,只要保证划分后三个区间的元素特性即可,[2,3,5,9,8,6]、[3,2,5,6,8,9]都可作为上述划分的结果
输入输出描述
第一行输入数组的长度n,第二行输入n个元素
输出划分后的结果
示例
输入:
10
5 1 9 2 5 7 4 5 3 6
输出:
1 2 4 3 5 5 5 9 7 6
# 快速排序的核心思路 O(n) S(1)
def solve(arr,L, R):
if L >= R:
return
lt = L
gt = R + 1
v = arr[L]
i = L + 1
while i < gt:
# 小于v
if arr[i] < v:
arr[i], arr[lt + 1] = arr[lt + 1], arr[i]
lt += 1
i += 1
# 等于v
elif arr[i] == v:
i += 1
# 大于v
else:
gt -= 1
arr[gt], arr[i] = arr[i], arr[gt]
arr[L], arr[lt] = arr[lt], arr[L]
print(arr)
# 同样操作左边区间
solve(arr, L, lt - 1)
# 同样操作右边区间
solve(arr, gt, R)
arr = [4,1,6,3,6,8,2,4,6,2,3,6,4,2,4,9,7,5,3,5,3,2,4,5,4,5,4]
solve(arr,0, len(arr) - 1)
print(arr)
练习06 长度最小的子数组
题目描述
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
输入输出描述
输入数组长度n和目标值target,接下来输入n个元素
输出最小子数组长度
示例
输入:
6 7
2 3 1 2 4 3
输出:
2
解释:
子数组 [4,3] 是该条件下的长度最小的子数组
# 思路1 暴力破解 枚举 O(n^2)
"""
优化思路:
1.过滤没有必要的计算
2.避免重复的计算
"""
"""
def sovle(arr, target):
min_length = len(arr) + 1
for i in range(len(arr)):
cur_sum = 0
for j in range(i, len(arr)):
sub_arr = arr[i:j + 1]
cur_sum += arr[j]
if cur_sum >= target:
min_length = min(len(sub_arr), min_length)
print(sub_arr, cur_sum)
break
if min_length != len(arr) + 1:
return min_length
else:
return 0
"""
# 思路2 O(n) S(1)
def sovle(arr, target):
left = 0
right = 0
cur_sum = arr[left]
min_length = len(arr) + 1
while True:
if cur_sum < target:
right += 1
if right >= len(arr):
break
cur_sum += arr[right]
else:
cur_length = right - left + 1
min_length = min(cur_length, min_length)
cur_sum -= arr[left]
left += 1
if left > right:
break
if min_length == len(arr) + 1:
return 0
else:
return min_length
arr = [2,3,1,2,4,3]
# i
# j
target = 7
ret = sovle(arr, target)
print(ret)
练习07 寻找两个正序数组中的中位数
题目描述
给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
输入输出描述
输入m和n,然后分别输入m个元素和n个元素
输出中位数
示例1
输入:
2 1
1 3
2
输出:
2.0
解释:
合并数组 = [1,2,3] ,中位数2
示例2
输入:
2 2
1 2
3 4
输出:
2.5
def solve(nums1, nums2):
length1 = len(nums1)
length2 = len(nums2)
if length1 > length2:
return solve(nums2, nums1)
if length1 == 0:
if length2 % 2 == 1:
return nums2[length2 // 2]
else:
return (nums2[length2 // 2] + nums[length2 // 2 - 1]) / 2
result = 0 # 中位数的结果
L = 0
R = length1
curA = 0
curB = 0
total = length1 + length2
while L <= R:
curA = (L + R) // 2
curB = (total + 1) // 2 - curA
L1 = -99999999 if curA == 0 else nums1[curA - 1]
R1 = 999999999 if curA == length1 else nums1[curA]
L2 = -99999999 if curB == 0 else nums2[curB - 1]
R2 = 999999999 if curB == length2 else nums2[curB]
if L1 > R2:
R = curA - 1
elif L2 > R1:
L = curA + 1
else:
if total % 2 == 0:
result = (max(L1, L2) + min(R1, R2)) / 2
else:
result = max(L1, L2)
break
return result
nums1 = [2,4,6,8]
nums2 = [1,3,5,7,9]
print(solve(nums1, nums2))
练习08 豆机器
题目描述
豆机器,也称为梅花或高尔顿盒子,它是一个统计实验的设备,它是由一个三角形直立板和均匀分布的钉子构成,如下图所示:

小球从板子的开口处落下,每次小球碰到钉子,它就是50%的可能掉到左边或者右边,最终小球就堆积在板子底部的槽内
编程程序模拟豆机器,提示用户输入小球的个数以及机器的槽数,打印每个球的路径模拟它的下落,然后打印每个槽子中小球的个数
输入输出描述
输入两个数据,分别表示小球个数和槽子的个数
输出每个小球经过的路径,和最终每个槽子里小球的个数(因为牵扯随机数,程序结果不唯一,示例仅用于表明题意)
示例
输入:
5 8
输出:
LRLRLRR
RRLLLRR
LLRLLRR
RRLLLLL
LRLRRLR
0 0 1 1 3 0 0 0
import random
# balls球的个数 sluts槽子的个数
def solve(balls, sluts):
# 定义槽子这个数组
arr = [0] * sluts
# 模拟每个球下落的路径
for _ in range(balls):
# 每个球下落几次?sluts - 1次
path = ""
# 模拟下落的过程 拼接路径
index = 0 # 这个球下落的在槽子中的角标
for _ in range(sluts - 1):
r = random.randint(0, 1)
if r == 0:
path += "L"
else:
path += "R"
# 计算小球下落的槽子角标
index += 1
print(path)
arr[index] += 1 # 累加槽子中小球的个数
return arr
balls, sluts = map(int, input().split(" "))
arr = solve(balls, sluts)
# 画图表示一下 柱状图
# pip list 查看python第三方库
# pip install XXX 安装XXX第三方库
import matplotlib.pyplot as plt
plt.bar(list(range(sluts)), arr)
plt.show()
练习09 四个连续的相同数字
题目描述
给定一个二维数组,判断其中是否有四个连续的相同数字,不管这四个数字是在水平方向、垂直方向还是斜线方向
输入输出描述
输入矩阵的行列n和m
输出YES表示存在,NO不存在
示例
输入:
5 5
5 6 2 1 6
6 5 6 6 1
1 3 6 1 4
3 6 3 3 4
0 6 2 3 2
输出:
YES
def is_lianxu(matrix, row, col):
length_row = len(matrix)
length_col = len(matrix[row])
# 向右
if col <= length_col - 4:
for c in range(col + 1, col + 4):
if matrix[row][col] != matrix[row][c]:
break
else:
print(matrix[row][col])
return True
# 向下
if row <= length_row - 4:
for r in range(row + 1, row + 4):
if matrix[row][col] != matrix[r][col]:
break
else:
print(matrix[row][col])
return True
# 向右下
if col <= length_col - 4 and row <= length_row - 4:
r = row + 1
c = col + 1
while r < row + 4 and c < col + 4:
if matrix[row][col] != matrix[r][c]:
break
r += 1
c += 1
else:
print(matrix[row][col])
return True
# 向左下
if row <= length_row - 4 and col >= 3:
r = row + 1
c = col - 1
while r < row + 4 and c > col - 4:
if matrix[row][col] != matrix[r][c]:
break
r += 1
c -= 1
else:
print(matrix[row][col])
return True
return False
def sovle(matrix):
for row in range(len(matrix)):
for col in range(len(matrix[row])):
if is_lianxu(matrix, row, col):
return True
return False
matrix = [
[5,6,2,1,6],
[3,5,6,6,1],
[1,3,6,1,4],
[3,6,3,3,4],
[0,6,2,3,2]
]
print(sovle(matrix))
练习10 找出最近距离的一对点
输入一组点的坐标,寻找哪两个点的距离最近。先输入点的个数 n,然后在输入 n 行点坐标。
示例1
输入:
8
-1 3
-1 -1
1 1
2 0.5
2 -1
3 3
4 2
4 -0.5
输出:
(1,1)与(2,0.5)最近
"""
8
-1 3
-1 -1
1 1
2 0.5
2 -1
3 3
4 2
4 -0.5
"""
n = int(input())
points = []
for _ in range(n):
point = list(map(float, input().split(" ")))
points.append(point)
distance = -1
p1 = None
p2 = None
# 开始计算两两之间点的距离
for i in range(len(points) - 1):
for j in range(i + 1, len(points)):
cur_p1 = points[i] # [-1, 3]
cur_p2 = points[j] # [-1, -1]
cur_dist = ((cur_p1[0] - cur_p2[0]) ** 2 + (cur_p1[1] - cur_p2[1]) ** 2) ** 0.5
if distance == -1:
distance = cur_dist
elif cur_dist < distance:
distance = cur_dist
p1 = cur_p1
p2 = cur_p2
print(p1)
print(p2)
print(distance)
练习11 探索二维矩阵 I
题目描述
给你一个满足下述两条属性的 m x n 整数矩阵:
- 每行中的整数从左到右按非递减顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。
输入输出描述
输入矩阵的行row和列col,和目标值target
接下来输入row行,每行col个元素
输出是否存在
示例
输入:
3 4 3
1 3 5 7
10 11 16 20
23 30 34 60
输出:
true
# 思路1 先卡区间 再对行进行二分查找
# def binary_search(arr, target):
# low = 0
# high = len(arr) - 1
# mid = (low + high) // 2
# while arr[mid] != target:
# if arr[mid] < target:
# low = mid + 1
# else:
# high = mid - 1
# if low > high:
# mid = -1
# break
# mid = (low + high) // 2
# return mid
# def solve(matrix, target):
# row = len(matrix) # 行数
# col = len(matrix[0]) # 列数
# # 先看是否存在与整个范围内
# if target < matrix[0][0] or target > matrix[row - 1][col - 1]:
# return -1, -1
# for i in range(row):
# line = matrix[i]
# if line[0] <= target <= line[col - 1]:
# return i, binary_search(line, target)
# matrix = [
# [1,3,5,7],
# [10,11,16,20],
# [23,30,34,60]
# ]
# target = 0
# x, y = solve(matrix, target)
# if x == -1 or y == -1:
# print("不存在")
# else:
# print(x, y)
# 思路2 将全体进行二分查找
def sovle(matrix, target):
row = len(matrix) # 行数
col = len(matrix[0]) # 列数
low = 0
high = row * col - 1
while low <= high:
mid = (high + low) // 2
cur = matrix[mid // col][mid % col]
if cur == target:
return True
elif cur < target:
low = mid + 1
else:
high = mid - 1
return False
matrix = [
[1,3,5,7],
[10,11,16,20],
[23,30,34,60]
]
target = 12
print(sovle(matrix, target))
练习12 拉丁方阵
拉丁方阵是指一个含有n个不同拉丁字母的 n × n 的方阵,每个字母在方阵的每一行和每一列都只出现一次。编写一个程序,程序提示用户输入方阵的大小 n,然后输入一个方阵,检测是否为拉丁方阵。方阵的字母是从A开始的n个字母。
示例1
输入:
4
A B C D
B A D C
C D B A
D C A B
输出:
是拉丁方阵
示例2
输入:
3
A B C
C A B
A C B
输出:
不是拉丁方阵
示例3
输入:
3
A F H
输出:
因为 n = 3,所以字母只能是A、B、C
"""
4
A B C D
B A D C
C D B A
D C A B
"""
def is_lading(matrix, aim):
# 取每一行
for i in range(len(matrix)):
line = matrix[i].copy()
line.sort()
if line != aim:
return False
# 取每一列
for j in range(len(matrix[0])):
line = []
for i in range(len(matrix)):
line.append(matrix[i][j])
line.sort()
if line != aim:
return False
return True
n = int(input())
matrix = []
for _ in range(n):
# "A B C D" => ["A","B","C","D"]
matrix.append(input().split(" "))
# 确定查找目标 n = 4 -> ["A","B","C","D"]
aim = [ chr(i + 65) for i in range(n)]
print(is_lading(matrix, aim))
练习13 五子棋游戏
五子棋是一种双人对弈的棋类游戏,双方分别使用黑白两色棋子,在棋盘上交替落子。游戏目标为在棋盘的横向、纵向或斜向任一方向上,使自己的五个同色棋子连成一线。通常先手执黑子,后手执白子,轮流在棋盘的交叉点上放置棋子,先达成五连珠的玩家获胜。
棋盘由纵横各 15 条线交叉组成,形成 225 个交叉点。棋子分为黑、白两色,黑子 113 枚,白子 112 枚。
提示用户每次下棋的坐标即可。
def create_board():
board = []
for _ in range(15):
line = []
for _ in range(15):
line.append("+")
board.append(line)
return board
def print_board(board):
for i in range(15):
for j in range(15):
print(board[i][j], end=" ")
print()
# 具体下棋的逻辑
def down_chess(board, chess, player):
x, y = map(int,input(f">>>请{player}下棋:").split(" "))
# 下棋之前需要判断一下此处是否已经有棋子了
if board[x - 1][y - 1] == "+":
board[x - 1][y - 1] = chess
print_board(board)
else:
print(">>>此处已有棋子,请重新下棋!")
down_chess(board, chess, player)
def is_game_over(board):
for row in range(15):
for col in range(15):
if board[row][col] != "+":
# 向右
if col < 11:
for c in range(col + 1, col + 5):
if board[row][col] != board[row][c]:
break
else:
return True
# 向下
if row < 11:
for r in range(row + 1, row + 5):
if board[row][col] != board[r][col]:
break
else:
return True
# 右下
if row < 11 and col < 11:
r = row + 1
c = col + 1
while r < row + 5 and c < col + 5:
if board[row][col] != board[r][c]:
break
r += 1
c += 1
else:
return True
# 左下
if row < 11 and col > 3:
r = row + 1
c = col - 1
while r < row + 5 and c > col - 5:
if board[row][col] != board[r][c]:
break
r += 1
c -= 1
else:
return True
return False
def start_game(board):
count = 0
while not is_game_over(board):
# 将棋子下到棋盘上
if count % 2 == 0:
down_chess(board, "●", "黑方")
else:
down_chess(board, "○", "白方")
count += 1
print(">>>游戏结束!")
def main():
# 创建棋盘
board = create_board()
# 打印棋盘
print_board(board)
# 开始游戏逻辑
start_game(board)
main()
"""
给棋盘加序号 在print_board加
和棋怎么办 数棋子数
最终谁赢 count
悔棋(限制悔棋一步) 栈
"""