代码随想录算法训练营第二天 | 209.长度最小的子数组 59.螺旋矩阵Ⅱ

LeetCode 209. 长度最小的子数组:

文章链接
题目链接:209.长度最小的子数组

看到题目后自己的思考

题目所求为长度最小的子数组,且子数组中的元素在原数组中应该是相邻的。
基于相邻这个特征和Carl中说可以用滑动窗口。我采取的方法是改变滑动窗口大小,其范围为1~len(list)。
双重循环来进行求解。第一层为滑动窗口大小,第二层为根据滑动窗口获取子数组,并判断子数组总和是否>=target。
一旦找到符合条件的子数组,返回当前滑动窗口的大小,否则返回0
需要注意的是
① csum = psum - psum开始 + cusm结束。而第一个窗口和不符合这个公式。那么,第二层循环中,要么将第一个窗口单独判断;要么从第二个窗口开始,循环开始前将第一个窗口和单独求解。
② 由于第一层循环中,窗口大小 - 1即第一个窗口的结束。因此fsum = fsum(前一次) + nums[l - 1]。
③ 但是fsum变量不能直接用于第二层循环中,因为第二层循环会改变fsum的大小,从而在下一次窗口大小改变时,fsum不再是fsum(前一次)。因此找到一个中间变量nsum用于第二层循环
④ 暴力的时间复杂度为O(n^2),在力扣中超时了。

class Solution(object):
    def minSubArrayLen(self, target, nums):
        lenn, presum = len(nums), 0 
        for l in range(1, lenn + 1):    # 滑动窗口的大小
            presum += nums[l - 1]   # presum为数组中第一个窗口中数组元素的总和
            if presum >= target:    # 第一个窗口的数组符合条件,返回当前窗口大小
                return l
            nsum = presum   # 当前窗口中数组元素的总和
            for i in range(l, lenn):    # 从第二个窗口开始遍历,i为窗口的结束位置
                nsum = nsum - nums[i - l] + nums[i]     # 当前窗口的和 = pre窗口和 - pre窗口开始 + 当前窗口结束
                if nsum >= target:  # 找到符合条件的数组,返回当前窗口大小
                    return l
        return 0    # 没找到

看完代码随想录后的思考

  1. 采用可变滑动窗口:也就是使用双指针i, j分别指向窗口起始和结束位置。
  2. 最开始窗口大小为1,通过移动 j 来扩大窗口大小,同时遍历列表
  3. 当窗口总和sum >= target时,不断移动 i 来缩小窗口,直到sum < target。同时在移动 i 的过程中,计算窗口大小。
  4. 对于每个元素来说,均有进入窗口和出窗口的操作,因此时间复杂度为O(2n) = O(n)

计算窗口大小1:一边缩小窗口,一边计算最小窗口大小

# 缩小窗口的同时计算最小窗口大小
class Solution(object):
    def minSubArrayLen(self, target, nums):
        i, nsum, min_len = 0, 0, float('inf')
        for j in range(len(nums)):
            nsum += nums[j]
            while nsum >= target:   # 缩小窗口大小同时计算最小数组长度
                min_len = min(min_len, j - i + 1)
                nsum -= nums[i]
                i += 1
        return min_len if min_len != float('inf') else 0

计算窗口大小2:先缩小窗口到临界,再计算最小窗口大小

# 当当前窗口和>=target时,直接移动 i 到临界位置,计算最小窗口大小
# 但是需要注意的是,计算窗口大小后,需要再移动一次 i 的位置,时当前窗口和<target
class Solution(object):
    def minSubArrayLen(self, target, nums):

        i, nsum, min_len = 0, 0, float('inf')
        for j in range(len(nums)):
            nsum += nums[j]
            if nsum >= target:  # 开始缩小窗口
                while (nsum - nums[i]) >= target:   # 缩小到临界值
                    nsum -= nums[i]
                    i += 1
                if min_len > (j - i + 1):
                    min_len = j - i + 1
                nsum -= nums[i]
                i += 1  # 最后缩小窗口到和<target
        return min_len if min_len != float('inf') else 0

实现过程中遇到的问题

  1. 前面实现暴力求解时,第一个窗口和的计算需要通过第一层循环叠加的方式实现。
  2. 使用双指针时,如果是先移动 i 到临界位置再计算窗口大小,没有注意到计算完成之后需要再移动一次 i 使得当前窗口和 < target

LeetCode59. 旋转矩阵Ⅱ:

文章链接
题目链接:59.旋转矩阵Ⅱ

开始看到题目的思路:

使用循环来实现,第一层循环是填充第 i 行所在的圈,第二层循环是填充当前圈的矩阵元素,通过找到当前圈的四个顶点的坐标实现。
i 的范围为0~ (n - 1) / 2

看完代码随想录之后:

整体思路与之前相同,但是在填充每一圈时,保持循环不变量,每填充一条边时,让出拐角作为下一条的开始。同时n为奇数时,最中间的元素既是开始也是拐角,需要额外填充。
下图中k从0开始,(,)为四个顶点的坐标
在这里插入图片描述

class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        matrix = [[0] * n for _ in range(n)]
        count, mid = 1, (n - 1) // 2
        for k in range(mid + 1):   # 从第0行所在的圈开始填充
            # 左到右
            for i in range(k, n - k - 1):
                matrix[k][i] = count
                count += 1
            # 上到下
            for i in range(k, n - k - 1):
                matrix[i][n - k - 1] = count
                count += 1
            # 右到左
            for i in range(n - k - 1, k, -1):
                matrix[n - k - 1][i] = count
                count += 1
            # 下到上
            for i in range(n - k - 1, k, -1):
                matrix[i][k] = count
                count += 1
        if n % 2 != 0:  # 奇数需要填充最中间的元素
            matrix[mid][mid] = count
        return matrix

实现过程中遇到的困难:

  1. 对于这种的模拟过程不是非常熟练,从而在构建遍历的过程中对于边界情况认识不太准确。
  2. 如果n为奇数,需要注意到最中间的元素需要额外赋值。
  3. 一些python的细节:
range(3, 1, -1)		print: 32
# 创建一个给定大小的二维数组
matrix = [[0] * n for _ in range(m)] 	# n是列数,m是行数

今日收获:

  1. 对于双指针的了解更为全面了。
  2. 应对类似螺旋矩阵这种考察对代码的掌控能力的题还需要进一步增强,在敲代码前先用纸币进行演算,同时注意循环不变量的使用。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值