2182 字
11 分钟
代码随想录算法训练营第二天 | 数组 Part02
数组基础
滑动窗口
滑动窗口是处理连续子数组或子序列问题的经典技巧,通常用于查找满足特定条件的最长或最短子数组。核心思想是通过维护一个窗口的左右边界,动态调整窗口大小来满足条件。
滑动窗口通用模板
def sliding_window(s): """ 滑动窗口通用模板 s: 输入数组或字符串 """ # 窗口状态记录:如果是频次问题,用字典;如果是总和问题,用整数 window = {} # 或 window = 0 (如求和)
left = 0 result = 初始化结果 # 如 0 / float('inf') / "" / [],根据题意设定
for right in range(len(s)): # 扩大窗口:将右边界元素加入窗口 c = s[right] # 更新窗口状态 window[c] = window.get(c, 0) + 1 # 或者 window += c(求和问题)
# ✅ 当寻找最大子数组/子串时,可以在这里尝试更新结果 # if 满足条件: # result = max(result, 当前窗口大小或值)
# 当窗口不满足题目要求的约束时,收缩左边界 while left <= right and 窗口不满足条件: # ✅ 当寻找最小子数组/子串时,应在收缩前更新结果 # if 满足条件: # result = min(result, 当前窗口大小或值)
# 收缩窗口:将左边界移出窗口 d = s[left] left += 1
# 更新窗口状态 window[d] -= 1 if window[d] == 0: del window[d] # 或 window -= d(求和问题)
# ✅ 当需要收集所有满足条件的子串/子数组时,在这里更新结果列表 # if 满足条件: # result.append(当前窗口或其他信息)
return result
209. 长度最小的子数组
题目要求:找到和大于等于target的最短连续子数组长度
class Solution: def minSubArrayLen(self, target: int, nums: List[int]) -> int: temp, left, res = 0, 0, float('inf')
for right in range(len(nums)): # 扩大窗口:将右边界元素加入窗口 temp += nums[right]
# 当窗口和满足条件时,尝试收缩左边界 # 需要考虑在什么时候需要缩小范围 # 并且可能一直缩小,导致这里只能使用while而不是if while temp >= target: # 寻找最小长度,在收缩窗口前更新结果 # 这时候temp必定大于等于target,更新结果 res = min(res, right - left + 1)
# 收缩窗口:将左边界元素移出窗口 temp -= nums[left] left += 1
return res if res != float('inf') else 0
904. 水果成篮
题目要求:找到最多包含两种不同水果的最长连续子数组
class Solution: def totalFruit(self, fruits: List[int]) -> int: left, wid, res = 0, {}, 0
for right in range(len(fruits)): # 扩大窗口:将右边界元素加入窗口 wid[fruits[right]] = wid.get(fruits[right], 0) + 1
# 当窗口中水果种类超过2种时,收缩左边界 while len(wid) > 2: # 收缩窗口:将左边界元素移出窗口 wid[fruits[left]] -= 1 if wid[fruits[left]] == 0: del wid[fruits[left]] left += 1
# 寻找最大长度,在窗口合法时更新结果 # 刚开始把res的更新放在while里面,当wid永远小于2的时候,res永远等于0,出现错误 res = max(res, right - left + 1)
return res
76. 最小覆盖子串
题目要求:找到包含字符串t所有字符的最小子串
class Solution: def minWindow(self, s: str, t: str) -> str: # 统计目标字符串中每个字符的需求数量 need = {} for char in t: need[char] = need.get(char, 0) + 1
# valid用于记录有多少种字符已经达到需求 left, min_len, valid, start = 0, float('inf'), 0, 0 wid = {} # 窗口中字符的计数
for right in range(len(s)): # 扩大窗口:将右边界字符加入窗口 c = s[right] wid[c] = wid.get(c, 0) + 1
# 当某种字符达到需求数量后才更新valid if c in need and wid[c] == need[c]: valid += 1
# 只有当窗口包含所有需求字符后才开始收缩 while valid == len(need): # 寻找最小长度,在收缩窗口前更新结果 if right - left + 1 < min_len: start = left min_len = right - left + 1
# 收缩窗口:将左边界字符移出窗口 d = s[left] left += 1
# 当移除的字符刚好使某种字符数量不满足需求时,需要将valid减1 if d in need and wid[d] == need[d]: valid -= 1 wid[d] -= 1
return "" if min_len == float('inf') else s[start: start + min_len]
二维数组的遍历
二维数组的遍历通常使用四个边界(上、下、左、右)来控制遍历方向。通过不断调整边界来实现螺旋或其他特定顺序的遍历。
二维数组螺旋遍历通用模板
def traverse_2d_array(matrix): # 二维数组螺旋遍历通用模板 if not matrix or not matrix[0]: return []
res = [] m, n = len(matrix), len(matrix[0]) up, down, left, right = 0, m - 1, 0, n - 1
while up <= down and left <= right: # 第一步:从左到右遍历上边界 for i in range(left, right + 1): res.append(matrix[up][i]) up += 1 # 上边界向下移动
# 第二步:从上到下遍历右边界 for i in range(up, down + 1): res.append(matrix[i][right]) right -= 1 # 右边界向左移动
# 第三步:从右到左遍历下边界(需要确保还有行) if up <= down: for i in range(right, left - 1, -1): res.append(matrix[down][i]) down -= 1 # 下边界向上移动
# 第四步:从下到上遍历左边界(需要确保还有列) if left <= right: for i in range(down, up - 1, -1): res.append(matrix[i][left]) left += 1 # 左边界向右移动
return res
59. 螺旋矩阵 II
题目要求:生成一个n×n的螺旋矩阵,按螺旋顺序填入1到n²的数字
class Solution: def generateMatrix(self, n: int) -> List[List[int]]: # 初始化n×n的零矩阵 res = [[0] * n for _ in range(n)]
# 使用四个边界来控制螺旋的方向 up, down, left, right = 0, n - 1, 0, n - 1 count = 1 # 要填入的数字
while count <= n * n: # 从左到右填充上边界 if up <= down: for i in range(left, right + 1): res[up][i] = count count += 1 up += 1
# 从上到下填充右边界 if left <= right: for i in range(up, down + 1): res[i][right] = count count += 1 right -= 1
# 从右到左填充下边界 if up <= down: for i in range(right, left - 1, -1): res[down][i] = count count += 1 down -= 1
# 从下到上填充左边界 if left <= right: for i in range(down, up - 1, -1): res[i][left] = count count += 1 left += 1
return res
54. 螺旋矩阵
题目要求:按螺旋顺序返回矩阵中的所有元素
class Solution: def spiralOrder(self, matrix: List[List[int]]) -> List[int]: res = [] m, n = len(matrix), len(matrix[0]) up, down, left, right = 0, m - 1, 0, n - 1
# 当结果数组长度小于矩阵总元素数时继续遍历 while len(res) < m * n: if up <= down: for i in range(left, right + 1): res.append(matrix[up][i]) up += 1 if left <= right: for i in range(up, down + 1): res.append(matrix[i][right]) right -= 1 if up <= down: for i in range(right, left - 1, -1): res.append(matrix[down][i]) down -= 1 if left <= right: for i in range(down, up - 1, -1): res.append(matrix[i][left]) left += 1
return res
前缀和
前缀和是一种预处理技巧,用于快速计算数组中任意区间的和。通过预先计算从数组开头到每个位置的累积和,可以在O(1)时间内计算任意区间和。
一维前缀和通用模板
def build_prefix_sum(arr): # 构建一维前缀和数组 n = len(arr) prefix = [0] * n temp = 0 for i in range(n): temp += arr[i] prefix[i] = temp return prefix
def range_sum(prefix, left, right): # 计算区间[left, right]的和 if left == 0: return prefix[right] return prefix[right] - prefix[left - 1]
58. 区间和
题目要求:给定数组和多个查询区间,快速计算每个区间的和
import sysinput = sys.stdin.read
def main(): data = input().split() index = 0
n = int(data[index]) index += 1 arr = [] for i in range(n): arr.append(int(data[index + i])) index += n
p = [0] * n temp = 0 for i in range(n): temp += arr[i] p[i] = temp
results = [] while index < len(data): a = int(data[index]) b = int(data[index + 1]) index += 2
if a == 0: sum_value = p[b] else: sum_value = p[b] - p[a - 1]
results.append(sum_value)
for res in results: print(res)
if __name__ == "__main__": main()
44. 开发商购买土地
题目要求:将土地分割成两部分,使得两部分价值差的绝对值最小
import sysinput = sys.stdin.read
def main(): data = input().split() index = 0
n, m = int(data[index]), int(data[index + 1]) index += 2 arr = [[0] * m for _ in range(n)] sum_val = 0
for i in range(n): for j in range(m): arr[i][j] = int(data[index]) sum_val += arr[i][j] index += 1
row, col = [0] * n, [0] * m for i in range(n): for j in range(m): row[i] += arr[i][j] for i in range(n): for j in range(m): col[j] += arr[i][j]
res = float('inf')
row_cut = 0 for i in range(n): row_cut += row[i] res = min(res, abs(sum_val - 2 * row_cut))
col_cut = 0 for i in range(m): col_cut += col[i] res = min(res, abs(sum_val - 2 * col_cut))
print(res)
if __name__ == "__main__": main()
代码随想录算法训练营第二天 | 数组 Part02
https://fuwari.vercel.app/posts/code_musings_day_two/