209. 长度最小的子数组
https://leetcode.cn/problems/minimum-size-subarray-sum/
思路(滑动窗口 or 蛄蛹者)
最先想到的思路就是暴搜,以每个元素作为区间的左端点,遍历后续所有元素作为右端点,分别统计出区间长度和区间内的元素总和,最后选出最符合条件的答案。这样做的本质就是将一个一维数组变成一个二维表,两轴都是数组所有元素的索引,表内存放的是对应区间长度和区间内的元素总和。显而易见,这种暴力方法的时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
下一步就是考虑有没有更快的方法,回看题目,发现暴力法的没有使用题目的一个条件:数组内全是正整数。这个条件意味着我们之前在遍历过程中,对一个确定的端点,区间长度越长,区间内元素总和就越大,这就可以考虑用滑动窗口来解决。
为了方便理解,我在这里把滑动窗口比作一条向前蛄蛹的虫,虫内的所有元素总和必须大于或等于 target。虫的头部(区间右端)不断向右移动,头每移动一次,尾(区间左端)就要跟上。如果区间内的所有元素总和 >= target,就记录区间长度并更新最小区间长度,然后尾继续跟上,直到区间内的所有元素总和小于 target。以此方法,头不断向前移动,尾随后跟上,最后头到达数组的末端,完成。
考虑时间复杂度,显而易见就是这条虫从最左端爬到了最右端,区间的左右两端分别遍历了一遍数组,所以是
O
(
n
)
O(n)
O(n)。
解答
根据上述思路,给出对应的如下代码:
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
n = len(nums)
results = n + 1
sumIn = 0
left = 0
# 确立区间的右端点为起始位置,不断向右遍历
for right in range(n):
sumIn += nums[right]
# 不断缩小区间的左端点,直到区间内的总和小于target
while sumIn >= target:
results = min(results, right - left + 1)
sumIn -= nums[left]
left += 1
# 如果results的值没有修改说明没找到对于区间
return results if results <= n else 0
时间复杂度:看似有两个循环,实际上上面已经分析过了,本质是区间的左右两端分别遍历了一遍数组,所以是 O ( n ) O(n) O(n)。
59. 螺旋矩阵 II
https://leetcode.cn/problems/spiral-matrix-ii/
思路
这题没有什么花里胡哨的算法,非常纯粹的“程序设计语言基础”而不是“数据结构与算法设计”。需要做的就是模拟填充矩阵的过程,以矩阵的主对角线上的元素为起始点,逐步画圈(有点像某道经典的一笔画题),直到所有的值都填满。思路简单清晰,但实际上手起来会发现有很多细节需要注意:
- 画圈的起点:每次画圈的起始点我们已经确立,就是主对角线,确定出这些元素的遍历:循环是从(0,0)开始,一直到(n // 2,n // 2)的位置(简单检验后发现,无论 n 的奇偶都成立),以此确定最外层循环的循环次数。
- 画圈的过程:画圈的过程我们需要拆分成从左到右、从上到下、从右到左、从下到上这样以此进行的四步。因此,我们来确立每一步的遍历。每一步的起点就是上一步的终点(或画圈的起点),所以主要考虑的是循环跳出条件:
(1)首先,为了对称,我们把每一步的遍历都设置为左闭右开,这样每一步都经过相同数目的格子;
(2)在这样的条件下,找到每一步经过格子数量和当前已经循环的次数、n的关系:

根据数学归纳法,得出每一步经过的格子数目为: ( n + 1 ) − 2 × l o o p s (n+1)-2\times loops (n+1)−2×loops。 - 最后,因为这种循环方式会使得 n 为奇数时,最中间的值没有被写入,因此需要补上。
解答
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0 for i in range(n)] for j in range(n)] # 不要用 matrix = [[0] * n] * n 初始化!
loops = 1
count = 1
while loops <= n // 2:
# 确定每次绕圈的起始位置
i = loops - 1
j = loops - 1
# 从左到右
for k in range(n + 1 - 2 * loops):
matrix[i][j] = count
j += 1
count += 1
# 从上到下
for k in range(n + 1 - 2 * loops):
matrix[i][j] = count
i += 1
count += 1
# 从右到左
for k in range(n + 1 - 2 * loops):
matrix[i][j] = count
j -= 1
count += 1
# 从下到上
for k in range(n + 1 - 2 * loops):
matrix[i][j] = count
i -= 1
count += 1
loops += 1
# 如果n是奇数,补充最中间的值
if n % 2 == 1:
matrix[n // 2][n // 2] = n * n
return matrix
时间复杂度:没辙, O ( n 2 ) O(n^2) O(n2)。
拓展
最开始写代码的时候,我对matrix的初始化为 matrix = [[0] * n] * n,这会导致一个问题,改变其中一个子列表的值,其他子列表的值也会随之改变。问题出在Python的浅拷贝上,以这种方式创建的列表,里面子列表的内存是指向同一块的,所以该一个其他也会跟着改动。最后改成:matrix = [[0 for i in range(n)] for j in range(n)] 才可以。

1536

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



