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. 长度最小的子数组#

LeetCode 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. 水果成篮#

LeetCode 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. 最小覆盖子串#

LeetCode 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#

LeetCode 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. 螺旋矩阵#

LeetCode 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. 区间和#

卡码网KamaCoder

题目要求:给定数组和多个查询区间,快速计算每个区间的和

import sys
input = 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. 开发商购买土地#

卡码网KamaCoder

题目要求:将土地分割成两部分,使得两部分价值差的绝对值最小

import sys
input = 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/
作者
Jarrett
发布于
2025-06-12
许可协议
CC BY-NC-SA 4.0