Day02 代码随想录刷题
知识点:双指针、滑动窗口、模拟
一、LeetCode977.有序数组的平方
1.解题思路
考点为双指针,据题意原数组nums是一个非递减、可能带负数的整数数组,所以每个元素平方后最大值肯定出现在数组两边,不是左边就是右边。
2.代码实现
我们通过双指针从两头向里搜索,找到平方后值最大的元素,再将该值存入新建的列表res
中去(从后往前排)。
根据输入数组的长度
len(nums)
新建列表res
,设计索引i
,从后向前储存,实现res
列表内元素的从小到大排列列表两头元素
nums[left]
和nums[right]
平方后的值大小判断可以通过两者相加实现,相加后为正,则右侧元素平方后值更大;否则左侧的负数平方后值更大。
class Solution:
def sortedSquares(self, nums: List[int]) -> List[int]:
# 双指针初始化
left, right = 0, len(nums) - 1
# 新建列表,内容初始化为 -1
res = [-1]*len(nums)
i = len(nums) - 1
while left <= right:
if nums[left] + nums[right] > 0:
res[i] = nums[right]**2
right -= 1
else:
res[i] = nums[left]**2
left += 1
i -= 1
return res
该解法相当于遍历了一遍数组nums
,时间复杂度为O(n)
,因为额外还维护了一个长度为n
的结果数组res
,所以空间复杂度也为O(n)
。
二、LeetCode209.长度最小的子数组
1.解题思路
考点为双指针实现滑动窗口,据题意可创建窗口右侧向右扩充,当窗口内元素满足要求后,窗口左侧开始向右收缩,期间找到满足要求的最小窗口长度min_len
。当左侧窗口收缩导致窗口内元素不满足要求时,窗口会继续向右侧扩充,扩充与收缩依次循环,直至窗口右侧遍历完整个列表元素。
实质:
窗口不达标,右侧扩充;
窗口达标了,左侧收缩;
上述2条不断循环,期间保存最小窗口的长度,窗口右侧遍历完列表时结束循环
2.代码实现
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
left = right = 0 # 初始化窗口左右侧
window_sum = 0
min_len = float("inf") # 窗口长度初始为无穷大
while right < len(nums): # 设置循环中止条件
window_sum += nums[right]
while window_sum >= target: # 窗口达标
min_len = min(min_len, right - left + 1)
window_sum -= nums[left]
left += 1# 窗口左缩进
right += 1# 窗口不达标,窗口右扩充
if min_len == float("inf"):
return 0
else:
return min_len
该解法额外设计了left
和right
两个指针,数组每个元素最多被操作2次,总共时间复杂度是2n
,所以时间复杂度是O(n)
,因为额外维护了2个指针,空间复杂度是O(1)
。
三、LeetCode059.螺旋矩阵II
1.解题思路
考点为模拟,螺旋问题有规律,确定边界后用循环实现。循环需要找到循环不变量,即每次循环填充一圈,一圈有四边。最外层的边界索引为0
到n-1
,我们每边只填充索引为0
到n-2
的位置即可实现每次边填充规律都一样。
填充数组时还有规律:
假设循环填充多圈,每循环
i
圈(i
从0
开始)后,每圈边界的填充索引开始点+i
,终止点-i
每轮循环向右、向下填充的索引一样,只是横行列不同;向左,向上类似
向右填充时,行不变,行是上边界
0
,每循环i
轮+i
;列变化,索引由0
到n-1
,每轮循环开始点+i
,终止点-i
向下填充时,列不变,列是右边界
n-1
,每循环i
轮-i
;行变化,索引由0
到n-1
,每轮循环开始点+i
,终止点-i
向左填充时,行不变,行是下边界
n-1
,每循环i
轮-i
;列变化,索引由n-1
到0
,每轮循环开始点-i
,终止点+i
向上填充时,列不变,列是左边界
0
,每循环i
轮+i
;行变化,索引由n-1
到0
,每轮循环开始点-i
,终止点+i
输入
n
为奇数时,矩阵存在中心点,该值也需要补充
2.代码实现
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
# 确定循环圈数
loop = n//2
# 初始化结果矩阵:n*n的零矩阵
res = [[0]*n for _ in range(n)]
# 递增数num
num = 1
for i in range(0,loop):
# i是循环轮数,a是每次边填充的索引
for a in range(i,n - 1 - i):
res[i][a] = num
num += 1
for a in range(i,n - 1 - i):
res[a][n - 1 - i] = num
num += 1
for a in range(n - 1 - i, i, -1):
res[n - 1 - i][a] = num
num += 1
for a in range(n - 1 - i, i, -1):
res[a][i] = num
num += 1
# 奇偶判断是否需要补充中心值
if n%2 == 1:
res[loop][loop] = num
return res
该解法模拟矩阵大小为n*n
且需要全部遍历填充,所以时间复杂度是O(n^2)
,此外还额外维护这个大小为n*n
的矩阵,所以空间复杂度为O(n^2)
。