1.长度最小的子数组(Leetcode 209)

(1)暴力解法:利用2个for循环直接遍历寻找,时间复杂度为O(n^2)
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
size, min_len = len(nums), float('inf')
for i in range(size):
sum = 0
for j in range(i, size):
sum += nums[j]
if sum >= target:
min_len = min(min_len, j-i+1)
break
return min_len if min_len != float('inf') else 0
!!!:此题用暴力解法会超出时间限制

(2)滑动窗口(双指针):使用两个指针 left 和 right 来表示当前窗口的左右边界,移动 right 指针来扩展窗口,累加窗口内的元素和。当窗口内的和大于或等于 s 时,尝试移动 left 指针来缩小窗口,以找到更小的子数组。在每次缩小窗口时,更新最小长度的记录。
时间复杂度:由于每个元素最多被访问两次(一次由 right 指针,一次由 left 指针),时间复杂度为 O(n)。
空间复杂度:只需要常数级别的额外空间来存储指针和累加和,空间复杂度为 O(1)。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
left, right, sum, size = 0, 0, 0, len(nums)
min_len = float('inf') #初始化最小长度为无穷大
for right in range(size): #滑动窗口右指针移动遍历
sum += nums[right] #计算当前窗口的和sum
while sum >= target and left <= right:
min_len = min(min_len, right - left +1 ) #更新滑动窗口大小
sum -= nums[left] #减去窗口最左侧值
left += 1 #左指针向右移
return min_len if min_len != float('inf') else 0
2.螺旋矩阵(Leetcode 59)

(1):我们使用一个 while 循环来填充矩阵,循环条件是 top <= bottom and left <= right,即只要上下边界和左右边界没有交叉,就继续填充。填充顺序为顺时针螺旋:顶行 → 右列 → 底行 → 左列。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0] * n for _ in range(n)] # 初始化一个 n x n 的矩阵,元素都为 0
top, bottom, left, right, num = 0, n-1, 0, n-1, 1 # 定义上下左右边界:top,bottom, left,right
while top <= bottom and left <= right: # 使用循环填充矩阵,按照顺时针螺旋顺序
for j in range(left, right + 1): # 填充顶行,从左到右
matrix[top][j] = num
num += 1
top += 1 # 顶行填充完毕,上边界下移
for i in range(top, bottom + 1): # 填充右列,从上到下
matrix[i][right] = num
num += 1
right -= 1 # 右列填充完毕,右边界左移
if top <= bottom: # 如果还有底行,填充底行,从右到左
for j in range(right, left - 1, -1):#从right到left递减向左填充,不包含left-1
matrix[bottom][j] = num
num += 1
bottom -= 1 # 底行填充完毕,下边界上移
if left <= right: # 如果还有左列,填充左列,从下到上
for i in range(bottom, top - 1, -1):#从bottom向top递减填充,不包含top-1
matrix[i][left] = num
num += 1
left += 1 # 左列填充完毕,左边界右移
return matrix
(2):定义一个偏移量offset, 用来控制每次循环的层数,作用是保留每次循环方向的最后一位,作为下个方向循环的起点,类似“左闭右开”的作用。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
nums = [[0]*n for _ in range(n)]
startx, starty = 0, 0 #起始点
loop, mid = n // 2, n // 2 #迭代次数,n为奇数时,矩阵的中心点
sum = 1 #矩阵的值
for offset in range (1, loop + 1): #每循环一层偏移量+1
for i in range(starty, n - offset): #从左至右,startx不变,starty变
nums[startx][i] = sum
sum += 1
for i in range(startx, n - offset): #从上至下,starty不变,startx变
nums[i][n - offset] = sum
sum += 1
for i in range(n - offset, starty, -1): #从右至左,startx不变,starty变
nums[n - offset][i] = sum
sum += 1
for i in range(n - offset, startx, -1): #从下至上,starty不变,startx变
nums[i][starty] = sum
sum += 1
startx += 1
starty += 1
if n % 2 !=0:
nums[mid][mid] = sum
return nums
3.卡码网(58. 区间和)

(1)解题思路:此题我们可以类比数列的前N项和公式,假设存在m<p<n,Sn-p = (Sn-m) - (Sp-m)。因此我们定义列表的前缀和prefix_sum,存在递推关系prefix_sum[i] = prefix_sum[i-1] + array[i],那么区间[a, b]的和就可以表示为prefix_sum[b] - prefix_sum[a-1]。
import sys
n = int(input()) # 读取数组长度 n
array = [int(input()) for _ in range(n)] # 读取 n 个整数存入数组
prefix_sum = [0] * n # 计算前缀和数组
prefix_sum[0] = array[0] # 第一个元素的前缀和即自身
for i in range(1, n):
prefix_sum[i] = prefix_sum[i-1] + array[i] # 前缀和递推
for line in sys.stdin: # 逐行读取直到文件结束
a, b = map(int, line.split()) # 读取区间起点 a 和终点 b
if a == 0: # 如果 a 为 0,直接输出 prefix_sum[b]
print(prefix_sum[b])
else:
print(prefix_sum[b] - prefix_sum[a-1])# 否则用prefix_sum[b]-prefix_sum[a-1]计算区间和
(2)暴力求解法:把指定区间的和都累加一遍,但是容易超时
4.卡码网(44. 开发商购买土地)

1.解题思路:同样运用前缀和的思想,给定一个 n x m 的网格,将其横向或纵向划分为两个子区域,使得两个区域的土地总价值之差最小。使用前缀和数组来快速计算子区域的和,然后枚举所有可能的横向和纵向划分,找出最小差值。
def min_land_value_diff(grid):
n, m = len(grid), len(grid[0])
total_value = sum(sum(row) for row in grid) # 计算整个区域的总土地价值
min_diff = float('inf') # 初始化最小差值为无穷大
# 计算前缀和,方便快速计算子区域的总价值
prefix_sum = [[0] * (m + 1) for _ in range(n + 1)]
for i in range(n):
for j in range(m):
prefix_sum[i + 1][j + 1] = (grid[i][j] + prefix_sum[i][j + 1] +
prefix_sum[i + 1][j] - prefix_sum[i][j])
# 竖直切割(按列切)
for col in range(1, m):
left_value = prefix_sum[n][col] # 左侧区域的总价值
right_value = total_value - left_value # 右侧区域的总价值
min_diff = min(min_diff, abs(left_value - right_value))
# 水平切割(按行切)
for row in range(1, n):
top_value = prefix_sum[row][m] # 上方区域的总价值
bottom_value = total_value - top_value # 下方区域的总价值
min_diff = min(min_diff, abs(top_value - bottom_value))
return min_diff
# 读取输入
def main():
n, m = map(int, input().split()) # 读取n和m
grid = [list(map(int, input().split())) for _ in range(n)] # 读取网格数据
# 计算并输出最小差值
print(min_land_value_diff(grid))
if __name__ == "__main__":
main()
2.代码解释
prefix_sum[i + 1][j + 1] = (grid[i][j] + prefix_sum[i][j + 1] +
prefix_sum[i + 1][j] - prefix_sum[i][j])
计算逻辑:
-
grid[i][j]:当前网格单元的土地价值。 -
prefix_sum[i][j + 1]:当前单元左侧的前缀和。 -
prefix_sum[i + 1][j]:当前单元上方的前缀和。 -
prefix_sum[i][j]:左上角区域的重复累加值,需要减去一次(因为它被前两项同时包含了)。
这样,每个 prefix_sum[i+1][j+1] 存储的是从 (0,0) 到 (i,j) 这个子矩阵的总和,从而可以在 O(1) 时间内求得任意子矩阵的总价值。
假设 grid 如下:
| 1 | 2 |
| 3 | 4 |
则 prefix_sum 计算如下:
| 0 | 0 | 0 |
| 0 | 1 | 3 |
| 0 | 4 | 10 |
这里 prefix_sum[2][2] = 10,表示整个矩阵的总和。
for col in range(1, m):
left_value = prefix_sum[n][col] # 左侧区域的总价值
这个循环用于按列进行竖直切割,计算左侧区域的总土地价值。
具体逻辑:
-
遍历所有可能的竖直切割位置:
col从1到m-1,意味着尝试所有可能的纵向分割方案。 -
计算左侧区域的总价值:
-
prefix_sum[n][col]代表从最上方 (0,0) 到最底部 (n-1,col-1) 这个矩形区域的总价值。 -
由于
prefix_sum预先计算了每个区域的累加和,这里可以 O(1) 计算左侧区域的总价值。
-
假设网格如下:
| 1 | 2 | 3 |
| 2 | 1 | 3 |
| 1 | 2 | 3 |
对应的 prefix_sum:
| 0 | 0 | 0 | 0 |
| 0 | 1 | 3 | 6 |
| 0 | 3 | 6 | 12 |
| 0 | 4 | 9 | 18 |
如果 col = 1,left_value = prefix_sum[3][1] = 4,意味着左侧部分如下:
| 1 | 2 | 3 |
|---|---|---|
| 2 | 1 | 3 |
| 1 | 2 | 3 |
&spm=1001.2101.3001.5002&articleId=146554566&d=1&t=3&u=e3f49641026a425f80a898f52caea785)
883

被折叠的 条评论
为什么被折叠?



