209 长度最小的子数组(前缀和+二分查找、滑动窗口)

18 篇文章 0 订阅
13 篇文章 0 订阅

1. 问题描述:

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

输入:s = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

进阶:

  • 如果你已经完成了 O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。

2. 思路分析:

① 因为需要在一个区间中找到和大于等于s的最小长度,所以这个是属于区间和的问题,所以一开始的想到的是使用前缀和的方法去解决,我们可以先遍历一遍数组,并且记录[0:i]区间的前缀和(0 <= i < len(nums)),然后枚举长度为1,2...k的区间和,检查是否大于等于s,假如找到了那么直接返回就可以了,但是提交上去超时了,因为在检查长度的时候需要检查每一个对应长度的区间,检查的时间复杂度就为O(n ^ 2)

② 官方的题解提供了两种可行的方法,第一种方法是前缀和 + 二分查找,第二种方法是滑动窗口,其中比较好理解的是滑动窗口的解法,下面是我自己的理解:

对于第一种方法,与①中方法类似也是使用的前缀和的方法但是不是漫无目的的枚举每一个长度为l的区间上的和,这里使用二分查找可能满足条件的区间,我们枚举的区间是[0:len(nums)],[1:len(nums)],[2:len(nums)],这样可以避免一些不必要的区间的搜索,这也是在代码中在二分查找的时候需要先加上当前位置前缀和的值的目的,这样在二分查找的时候可以每一次都是在上一次查找的长度减1的前提下去查找

③ 第二种方法是滑动窗口的思路,一开始的时候竟然没有想到,我们需要找到一个区间和大于等于的s的最小长度,可以先从索引为0的这个位置开始找,然后找到和大于等于s的这个位置,这个时候可以让左边界向右扩大一点看区间和是否还是大于s,假如满足和大于等于s的时候更新最小长度,当发现左边界向右扩大之后和小于了s,这个时候就需要往右扩大右边界了,整个过程就是扩大与缩小窗口的过程,思路还是挺好理解的

3. 代码如下:

官方的前缀和+二分查找解法:

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0
        n = len(nums)
        ans = n + 1
        sums = [0]
        for i in range(n):
            sums.append(sums[-1] + nums[i])
        for i in range(1, n + 1):
            target = s + sums[i - 1]
            bound = bisect.bisect_left(sums, target)
            if bound != len(sums):
                ans = min(ans, bound - (i - 1))
        return 0 if ans == n + 1 else ans

滑动窗口解法:

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0
        n = len(nums)
        ans = n + 1
        start, end = 0, 0
        total = 0
        while end < n:
            total += nums[end]
            while total >= s:
                ans = min(ans, end - start + 1)
                total -= nums[start]
                start += 1
            end += 1
        return 0 if ans == n + 1 else ans
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值