- LeetCode 977.有序数组的平方(补)
- LeetCode 209.长度最小的子数组
- LeetCdoe 59.螺旋矩阵II
前言
第二天了,继续坚持!
LeetCode 977.有序数组的平方(补)
977-1 原始想法
拿到题目,原数组可能出现负数,要输出non-decreasing的平方数组,直接平方+排序的暴力方法肯定不优雅,考虑使用双指针法。但是,参照第一天LeetCode27的快慢指针也不行,因为排序是一个逐步比较“行进”的过程,当slow包含的数组大小到了2个以上,就要逐个覆写数组的数值,时间复杂度激增。
977-2 随想录提醒
这一次派上了首尾指针法派上了用场。由于题目明确说明原始数组也是non-decreasing的,数组各元素平方之后肯定是从两头到中间递减的,所以只需要指针从数组的两端开始,逐个比对当前指针所指向的元素大小,在放入一个与nums大小相同的新数组result当中即可。执行操作之后,对指针继续更新移位,直至两个指针相遇,就遍历完了所有元素,复杂度为O(n)
977-3 最终题解
class Solution(object):
def sortedSquares(self, nums):
"""
:type nums: List[int]
:rtype: List[int]
"""
left, right, loc = 0, len(nums)-1, len(nums)-1
result = [float('inf')] * len(nums)
# result = nums # 不能直接这样写,这样会造成混乱
while (left <= right):
if nums[left]**2 <= nums[right]**2:
result[loc] = nums[right]**2
right -= 1
else:
result[loc] = nums[left]**2
left += 1
loc -= 1
return result
977-4 Debug过程
上面已经在代码中注明了,不能采用result = nums的写法,因为python中对数组直接采用赋值运算符(也就是“=”)是对原有数组的引用,并非我们以为的复制。可以参考这篇博客
LeetCode 209.长度最小的子数组
209-1 原始想法
看到本题很容易想到的就是暴力解法:使用两个for循环嵌套,外层表示区间的起始索引,内层是区间的终止索引,这样也能解出答案,但是效率太低了。
209-2 随想录提醒
使用滑动窗口法(其实和前面提到双指针法本质是一样的,都是比较数据,实现对区间端口索引的更新)。
关于滑动窗口法与暴力法的不同,我的理解是,前者是后者的剪枝,充分运用了“数组在物理上是连续的”这一特性。可能有点绕,下面是详细解释:如果从算法运行的初始几步看,窗口法和暴力法是一致的,起始结点都是0,终止结点依次增加,直到超过或者达到target,之后不同于暴力法继续进入for循环归零“重开”,窗口法采用的是"储存+迭代"的思路,区间内部的元素和用sum来储存(个人觉得这是窗口法的关键一招),变更区间后利用min函数比较新区间的sum和上一区间sum的大小,一路迭代更新,避免了多次重复计算,最终输出全局最小的结果。这样的复杂度只有O(n),也就是把数组遍历一遍的资源。
如果把握住算法的动态性,可以这么看:所选区间的变化无法就两种,即有时向右扩大,有时自左收缩。当然,具体的理解,还是得看代码和随想录的gif。
209-3 最终题解
class Solution(object):
def minSubArrayLen(self, target, nums):
"""
:type target: int
:type nums: List[int]
:rtype: int
"""
left, subL = 0, len(nums)+1
sumValue = 0
for right in range(0, len(nums)): # 这里与暴力法的两层循环是一样的,都是对数组进行遍历
sumValue += nums[right]
while(sumValue >= target): # 这是十分关键的地方
subL = min(subL, right-left+1)
sumValue = sumValue - nums[left]
left += 1
return subL if subL!=len(nums)+1 else 0
209-4 Debug过程
一开始没有理解滑动窗口法的内核是“用一层遍历循环干两层循环的活”,尤其是上面标注的for循环中while循环的写法,导致和双指针法混淆了。
插一句灵魂拷问:为什么是滑动窗口法的时间复杂度是O(n)?
随想录中也有解释,这里补充以下:要注意与两个for循环嵌套不同,while中的for循环不一定在每次for循环中都会走入,因此不能简单地认为时间复杂度就是n×n。宏观来看,在最坏情况,算法终点时left和right指针同时指向数组的最后一个元素,这一情景下,窗口对每个元素都完成了一次“纳入”和“吐出”的操作,故复杂度时2×n,即O(n)。
LeetCode 59. 螺旋矩阵II
59-1 原始想法
这道题没什么好说的吧,就纯场景模拟,考的就是对代码边界条件的掌控。
59-2 随想录提醒
和二分查找的易错点一样,要注意区间是左闭有开还是左开右闭,这会影响边界条件的处理。显然,上下左右四个方位的区间模式要是一致的,要么左闭右开,要么左开右闭。
59-3 最终题解
class Solution(object):
def generateMatrix(self, n):
"""
:type n: int
:rtype: List[List[int]]
"""
i, counter = 0, 1
offset = 1
Matrix = [[float('inf')]*n for _ in range(n)]
while offset <= n//2:
for j in range(offset-1, n-offset):
Matrix[offset-1][j] = counter
counter += 1
for i in range(offset-1, n-offset):
Matrix[i][j+1] = counter
counter += 1
for j in range(n-offset, offset-1, -1):
Matrix[i+1][j] = counter
counter += 1
for i in range(n-offset, offset-1, -1):
Matrix[i][j-1] = counter
counter += 1
offset += 1
if n%2==1: Matrix[n//2][n//2]=counter # 如果n为奇数,那么上面的四个for循环无法照顾到矩阵正中间的元素
return Matrix
59-4 Debug过程
for循环的边界条件一如既往的难调,还是得多练吧!