209.长度最小的子数组
力扣题目链接(opens new window)
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。
示例:
-
输入:s = 7, nums = [2,3,1,2,4,3]
-
输出:2
-
解释:子数组 [4,3] 是该条件下的长度最小的子数组。
提示:
-
1 <= target <= 10^9
-
1 <= nums.length <= 10^5
-
1 <= nums[i] <= 10^5
代码
首先我第一个念头是kmp那种方法,但不知道怎么做。我能想到如果找长度最小的子数组,我需要记录前几个数和的状态,但是我没想到通过滑动窗口去(双指针)去不断移动数和。我想的还是像是前数和那种。
class Solution:
def minSubArrayLen(self, target: int, nums: list[int]) -> int:
s = float('inf')
l, r = 0, 0
sum = 0
for r in range(len(nums)):
# print("l:", l, " " + "r: ", r)
sum += nums[r]
while sum >= target: # 若达到目标值
s = min(s,r-l+1)
# print(s)
sum -= nums[l]
l += 1
return 0 if s == float('inf') else s
solution = Solution()
nums = [1,1,1,1,1,1,1,1]
target = 11
s = solution.minSubArrayLen(target, nums)
print(s) # 输出应该是0
59.螺旋矩阵II
力扣题目链接(opens new window)
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
代码
一开始我没有完全明白左闭右开的意思,第一次循环准备把三块方块全涂上。但是我马上发现这样子这次循环的最后一边会只有一个方块。感觉很难弄(其实是思路错了)。
然后我想了想检测前方路径的办法,就是如果前方的数是0,就更改,如果不是就顺时针旋转,但是这样子我没想到越界的问题该如何处理(之后我发现可以给循环的每条边弄一个上下左右边界border,让他们涂到墙就自动旋转。
最后我参考的是代码随想录里的。如图
class Solution:
def generateMatrix(self, n: int) -> list[list[int]]:
nums = [[0 for i in range(n)] for i in range(n)] # x是纵轴,y是横轴
count = 1
loop = n // 2 # 循环几圈
start = 0
mid = n // 2
offset = 1 # 循环第几圈
while (loop):
for j in range(start, n-offset): # 横列
nums[start][j] = count
count += 1
for i in range(start, n-offset):
nums[i][n-offset] = count
count += 1
for j in range(n - offset, start, -1):
nums[n - offset][j] = count
count += 1
for i in range(n - offset, start, -1):
nums[i][start] = count
count += 1
loop -= 1
start += 1
offset += 1
if n % 2 == 1:
nums[mid][mid] = n ** 2
return nums
solution = Solution()
n = 3
arr = solution.generateMatrix(n)
print(arr)
设置loop来显示当前是第几次循环(每次循环画出正方形的四条边)设置n-offset来控制边长,设置start来控制此次loop的起点,最后考虑到n可能为奇数的情况,在为奇数的情况手动填充中心。
但是之后我看了下力扣中K神的代码,我感觉他的更简约,思路更明确。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
# 初始化边界
l, r, t, b = 0, n - 1, 0, n - 1
# 创建 n x n 的二维列表并初始化为 0
mat = [[0 for _ in range(n)] for _ in range(n)]
# 初始值和目标值
num, tar = 1, n * n
while num <= tar:
# 从左到右填充上边
for i in range(l, r + 1):
mat[t][i] = num
num += 1
t += 1 # 上边界下移
# 从上到下填充右边
for i in range(t, b + 1):
mat[i][r] = num
num += 1
r -= 1 # 右边界左移
# 从右到左填充下边
for i in range(r, l - 1, -1):
mat[b][i] = num
num += 1
b -= 1 # 下边界上移
# 从下到上填充左边
for i in range(b, t - 1, -1):
mat[i][l] = num
num += 1
l += 1 # 左边界右移
return mat # 返回生成的矩阵
58. 区间和
本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
题目描述
给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。
输入描述
第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。
输出描述
输出每个指定区间内元素的总和。
输入示例
5
1
2
3
4
5
0 1
1 3
输出示例
3
9
数据范围:
0 < n <= 100000
代码
学过前缀和,一看直接秒杀了,改改代码就好。
# 读取所有输入,并按空格分割成列表
data = input().split()
# 初始化索引变量
index = 0
# 读取数组长度 n
n = int(data[index])
index += 1 # 更新索引
# 初始化数组 vec
vec = []
# 初始化前缀和数组 p
p = [0] * n
for i in range(n):
# 从数据中读取数组元素并添加到 vec 中
vec.append(int(data[index + i]))
p[i] = vec[i] + p[i-1] #p[i-1]当i=0时是最后一个元素
index += n # 更新索引
# 初始化结果列表
results = []
# 处理查询
while index < len(data):
# 读取查询区间的起始索引 a 和结束索引 b
a = int(data[index])
b = int(data[index + 1])
index += 2 # 更新索引
# 计算区间 [a, b] 的和
if a == 0:
# 如果 a 是 0,直接使用 p[b] 作为结果
sum_value = p[b]
else:
# 否则,计算 p[b] - p[a-1]
sum_value = p[b] - p[a - 1]
# 将结果添加到 results 列表中
results.append(sum_value)
# 输出每个查询的结果
for result in results:
print(result)
44. 开发商购买土地
本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
题目链接(opens new window)
【题目描述】
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。
为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
【输入描述】
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述
请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
【输入示例】
3 3 1 2 3 2 1 3 1 2 3
【输出示例】
0
【提示信息】
如果将区域按照如下方式划分:
1 2 | 3
2 1 | 3
1 2 | 3
两个子区域内土地总价值之间的最小差距可以达到 0。
【数据范围】:
-
1 <= n, m <= 100;
-
n 和 m 不同时为 1。
代码
看到这个正方形矩阵,我第一反应就是横纵轴前缀和,但是我不知道该如何计算最小切割差值。代码给的方法是弄个总和,再循环一列列求。代码是
diff = abs(total_sum - 2 * horizontal_cut)
我的想法是给横纵轴前缀和再弄个前缀和,这样就能快速计算差值。感觉代码给的方法在时间复杂度上会快一点,因为并不需要多次查找,只需要一次找最小差距
import sys
input = sys.stdin.read
# 读取所有输入,并按空格分割成列表
data = input().split()
# 初始化索引变量
idx = 0
# 读取矩阵的行数 n 和列数 m
n = int(data[idx])
idx += 1
m = int(data[idx])
idx += 1
# 初始化总和 sum
total_sum = 0
# 初始化二维数组 vec
vec = []
# 读取矩阵的元素
for i in range(n):
row = []
for j in range(m):
num = int(data[idx])
idx += 1
row.append(num)
total_sum += num
vec.append(row)
# 统计每行的和
horizontal = [0] * n
for i in range(n):
for j in range(m):
horizontal[i] += vec[i][j]
# 统计每列的和
vertical = [0] * m
for j in range(m):
for i in range(n):
vertical[j] += vec[i][j]
# 初始化结果为无穷大
result = float('inf')
# 计算水平切割的最小差值
horizontal_cut = 0
for i in range(n):
horizontal_cut += horizontal[i]
# 计算当前切割的差值
diff = abs(total_sum - 2 * horizontal_cut)
# 更新结果
result = min(result, diff)
# 计算垂直切割的最小差值
vertical_cut = 0
for j in range(m):
vertical_cut += vertical[j]
# 计算当前切割的差值
diff = abs(total_sum - 2 * vertical_cut)
# 更新结果
result = min(result, diff)
# 输出结果
print(result)
总结
二分法
循环不变量原则,只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节。
双指针法
双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 暴力解法时间复杂度:O(n^2)
- 双指针时间复杂度:O(n)
滑动窗口
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。
模拟行为
真正解决题目的代码都是简洁的,或者有原则性的