【Day2】LeetCode:209. 长度最小的子数组,59. 螺旋矩阵 II

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/

思路

这题没有什么花里胡哨的算法,非常纯粹的“程序设计语言基础”而不是“数据结构与算法设计”。需要做的就是模拟填充矩阵的过程,以矩阵的主对角线上的元素为起始点,逐步画圈(有点像某道经典的一笔画题),直到所有的值都填满。思路简单清晰,但实际上手起来会发现有很多细节需要注意:

  1. 画圈的起点:每次画圈的起始点我们已经确立,就是主对角线,确定出这些元素的遍历:循环是从(0,0)开始,一直到(n // 2,n // 2)的位置(简单检验后发现,无论 n 的奇偶都成立),以此确定最外层循环的循环次数。
  2. 画圈的过程:画圈的过程我们需要拆分成从左到右、从上到下、从右到左、从下到上这样以此进行的四步。因此,我们来确立每一步的遍历。每一步的起点就是上一步的终点(或画圈的起点),所以主要考虑的是循环跳出条件:
    (1)首先,为了对称,我们把每一步的遍历都设置为左闭右开,这样每一步都经过相同数目的格子;
    (2)在这样的条件下,找到每一步经过格子数量和当前已经循环的次数、n的关系:
    在这里插入图片描述
    根据数学归纳法,得出每一步经过的格子数目为: ( n + 1 ) − 2 × l o o p s (n+1)-2\times loops (n+1)2×loops
  3. 最后,因为这种循环方式会使得 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)] 才可以。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值