代码随想录算法训练营第二天 | 977.有序数组的平方,209.长度最小的子数组,59.螺旋矩阵II

加班到晚上十点钟回来实在刷不动题了,第二天早上起来摸鱼刷题,否则要追不上了!

977.有序数组的平方

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

一开始没看懂题,直接无脑写成了这样。但是这里有个低级问题,就是range是左闭右开,所以这里应该写的是len(nums)不用减一。

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        for i in range(len(nums)-1):
            nums[i] = nums[i] * nums[i]
        return nums

然后果然运行不通过,发现原来还要排序。考虑到我现在还没写过排序题,之前学的冒泡排序也忘差不多了, 时间又有限,决定先直接sort一个,居然通过了。

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        for i in range(len(nums)):
            nums[i] = nums[i] * nums[i]
        list.sort(nums)
        return nums

但是正经解法还是要学的,直接看讲解。

讲解链接:https://www.bilibili.com/video/BV1QB4y1D7ep

一眼看到双指针这个关键词,前天才刷过,感觉之后应该要总结一下双指针的思路是什么了。

1.重要的数据特征:因为有负数,同时是非递减数组,最大元素肯定在两边。

2.双指针可以从两边往中间走,也就是从大到小取。然后对于我们新建的数组,下标从右向左开始更新就行了。

看完第一遍写成了这样,运行直接爆炸。检查了之后发现问题出在vec的定义上,只更新了后两位。因为有两个指针,所以while的轮数是len(nums)/2,这就是问题。

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        vec = nums
        i = 0
        j = len(nums) - 1
        k = len(nums) - 1
        while i <= j:
            if nums[i]*nums[i] > nums[j]*nums[j]:
                vec[k] = nums[i]*nums[i]
                k-=1
                i+=1
            else:
                vec[k] = nums[j]*nums[j]
                k-=1
                j-=1
        return vec


把vec换成空列表的定义,运行通过。

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        vec = [float('inf')] * len(nums)
        i = 0
        j = len(nums) - 1
        k = len(nums) - 1
        while i <= j:
            if nums[i]*nums[i] > nums[j]*nums[j]:
                vec[k] = nums[i]*nums[i]
                k-=1
                i+=1
            else:
                vec[k] = nums[j]*nums[j]
                k-=1
                j-=1
        return vec
209.长度最小的子数组

题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

训练营提示让直接看题解,于是照做了,题解链接:https://www.bilibili.com/video/BV1tZ4y1q7XE

关键词是滑动窗口和双指针,又是双指针……我现在把它理解为是在一个循环中同时进行两个循环的过程,可以并列、对冲之类的。。两个指针中间的部分就是滑动窗口了。

for循环里面的j表示的是终止位置,然后起始位置i在里面动态移动。看完有点似懂非懂,于是直接看答案,又让GPT给我讲了一遍:

(版本一)滑动窗口法
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        l = len(nums)
        left = 0
        right = 0
        min_len = float('inf')
        cur_sum = 0 #当前的累加值
        
        while right < l:
            cur_sum += nums[right]
            
            while cur_sum >= s: # 当前累加值大于目标值
                min_len = min(min_len, right - left + 1)
                cur_sum -= nums[left]
                left += 1
            
            right += 1
        
        return min_len if min_len != float('inf') else 0

下面是代码的解读:

  1. 初始化变量:

    • l表示整数列表nums的长度。
    • leftright表示滑动窗口的左右指针,初始都指向列表的第一个元素。
    • min_len用于记录最短子数组的长度,初始值设为正无穷。
    • cur_sum表示当前的累加值,初始值为0。
  2. 进入while循环,条件是right小于列表的长度。

  3. 在循环中,首先将当前元素nums[right]添加到累加值cur_sum中。

  4. 进入第二个while循环,条件是当前累加值cur_sum大于等于目标值s

    • 在这个循环中,我们更新最短子数组的长度min_len,通过将当前窗口的长度right - left + 1min_len比较并取较小值。
    • 然后,从当前累加值中减去左指针指向的元素nums[left],并将左指针向右移动一位left += 1,以缩小窗口的大小。
  5. 无论是否进入第二个while循环,都将右指针向右移动一位right += 1,扩大窗口的大小。

  6. 循环结束后,返回最短子数组的长度min_len,如果min_len仍然是正无穷,则返回0,表示没有找到满足条件的子数组。

这段代码的核心思想是使用两个指针来表示滑动窗口的左右边界,通过不断调整窗口的大小,找到满足条件的最短子数组。

用同样的方法过了一遍暴力解法。还是头一次看到这样写for循环的,内层循环的开头是外层循环的索引……

(版本二)暴力法
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        l = len(nums)
        min_len = float('inf')
        
        for i in range(l):
            cur_sum = 0
            for j in range(i, l):
                cur_sum += nums[j]
                if cur_sum >= s:
                    min_len = min(min_len, j - i + 1)
                    break
        
        return min_len if min_len != float('inf') else 0

下面是代码的解读:

  1. 初始化变量:

    • l表示整数列表nums的长度。
    • min_len用于记录最短子数组的长度,初始值设为正无穷。
  2. 进入外层循环,遍历整数列表nums中的每个元素,使用变量i表示当前元素的索引。

  3. 在外层循环中,初始化当前累加值cur_sum为0。

  4. 进入内层循环,从当前索引i开始遍历到列表的末尾。

    • 在内层循环中,将当前元素nums[j]添加到累加值cur_sum中。
    • 如果当前累加值cur_sum大于等于目标值s,则满足条件,更新最短子数组的长度min_len,通过将当前子数组的长度j - i + 1min_len比较并取较小值。
    • 然后跳出内层循环,因为我们已经找到了满足条件的最短子数组。
  5. 外层循环继续进行,遍历下一个元素。

  6. 循环结束后,返回最短子数组的长度min_len,如果min_len仍然是正无穷,则返回0,表示没有找到满足条件的子数组

59. 螺旋矩阵Ⅱ

一眼看过去还是不知道怎么解,于是直接看视频讲解,链接:https://www.bilibili.com/video/BV1SL4y1N7mV/?vd_source=40781b3117fa72cc67da258629107b62

真的是一圈一圈写的,原理理解了,代码不理解,于是直接看答案:

讲解中写的是,这个题和二分法一样,并不涉及到什么算法,就是模拟过程,需要坚持循环不变量原则。看代码是直接看懂了,但是没有动手写,时间来不及了。

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为奇数时,矩阵的中心点
        count = 1                           # 计数

        for offset in range(1, loop + 1) :      # 每循环一层偏移量加1,偏移量从1开始
            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums[startx][i] = count
                count += 1
            for i in range(startx, n - offset) :    # 从上至下
                nums[i][n - offset] = count
                count += 1
            for i in range(n - offset, starty, -1) : # 从右至左
                nums[n - offset][i] = count
                count += 1
            for i in range(n - offset, startx, -1) : # 从下至上
                nums[i][starty] = count
                count += 1                
            startx += 1         # 更新起始点
            starty += 1

        if n % 2 != 0 :			# n为奇数时,填充中心点
            nums[mid][mid] = count 
        return nums

训练营提示说让做个数组总结,说实在的我实在是太菜了不会总结,于是去看了训练营的总结:https://programmercarl.com/%E6%95%B0%E7%BB%84%E6%80%BB%E7%BB%93%E7%AF%87.html#%E6%95%B0%E7%BB%84%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80

二分法:循环不变量原则,需要在循环中坚持对区间的定义,是算法面试中的常考题。

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。

滑动窗口:精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

模拟行为:真正解决题目的代码都是简洁的,或者有原则性的

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值