前缀和 | 双指针 | 二分查找:力扣209. 长度最小的子数组

1、题目描述:

在这里插入图片描述

2、题解:

方法1:辅助队列
思路:

设置一个队列,把数组的首元素放进去,
然后遍历[1,n),n为数组的长度:
   如果队列的元素之和小于s就继续往队列添加数组中的元素
   循环处理队列的元素之和不小于s的情况:
      选择满足条件的最短的连续子数组
      把队列的首元素出队列
最后返回最小值(要判断res是否改变)

代码如下:

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        #辅助队列
        if not nums:
            return 0
        res = float('inf')
        temp = [nums[0]]
        n = len(nums)
        for i in range(1,n):
            if sum(temp) < s:
                temp.append(nums[i])
            while sum(temp) >= s:
                res = min(res,len(temp))
                temp.pop(0)
        return res if res != float('inf') else 0

方法2:双指针法
辅助队列每次都要计算队列元素之和,浪费了时间,进行优化,考虑使用双指针法
其实大致思路是一样的,只不过我们不设置辅助队列,而是一个sum_,避免了重复计算队列的元素之和,能加快运算。
思路:

n = len(nums),为数组的长度
设置left,right两个指针,sum_是两个指针的之间的元素之和,res = float('inf')设置res为最大值
循环条件是right< n:
	如果sum_<s:
		让sum_加上right指针所指的元素
		并且令right指针向右移动一次
	循环处理sum_ >= s的情况:
		先让sum_减去left指针所指的元素
		保留res和right-left两者的最小值
		令left指针向右移动一次
最后返回res,如果res的值不是一开始设置的最大值

如下图:上面一行是双指针法的结果,下面一行是辅助队列的结果,很容易就可以看到双指针法运行时间得到很大的优化
在这里插入图片描述

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        #双指针法
        if not nums:
            return 0
        n = len(nums)
        left,right = 0,0
        res = float('inf')
        sum_ = 0
        while right < n:
            if sum_ < s:
                sum_ += nums[right]
                right += 1
            while sum_ >= s:
                sum_ -= nums[left]
                res = min(res,right - left)
                left += 1
        return res if res != float('inf') else 0

方法3、二分查找:
题目说了用O(NlogN)的时间复杂度,想到了二分查找,然后需要有序。这时候需要定义一个有序数组。
有序数组sums[i],代表[0,i]的累计和。
然后去用二分查找法去做就行。
先写出暴力法的优化版本,但是python还是超时

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        #优化暴力法,超时
        if not nums:
            return 0
        n = len(nums)
        sums = [0] * n
        sums[0] = nums[0]
        for i in range(1,n):
            sums[i] = nums[i] + sums[i - 1]
        res = float('inf')
        for i in range(n):
            s2 = s - nums[i] #除去当前数字
            for j in range(i,n):
                if sums[j] - sums[i] >= s2:
                    res = min(res,j - i + 1)
        return res if res != float('inf') else 0

二分查找,也即是修改内层的for循环,对于sums[j] - sums[i] >= s2,通过移项,也就是sums[j] >= s2 + sums[i] ,含义就是寻找一个sums[j],使得其刚好大于等于s2 + sums[i]。因为sums是个有序数组,所有找sum[j]可以采取二分的方法。

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        #二分查找
        if not nums:
            return 0
        n = len(nums)
        sums = [0] * n
        sums[0] = nums[0]
        for i in range(1,n):
            sums[i] = nums[i] + sums[i - 1]
        res = float('inf')
        for i in range(n):
            s2 = s - nums[i] #除去当前数字
            #二分查找,目标值是s2 + sums[i]
            k = self.binarySearch(i,n-1,sums,s2 + sums[i])
            if k != -1:
                res = min(res,k - i + 1)
        return res if res != float('inf') else 0
    def binarySearch(self,start,end,sums,target):
        while start <= end:
            mid = start + (end - start) // 2
            if sums[mid] == target:
                return mid
            elif sums[mid] < target:
                start = mid + 1
            else:
                end = mid - 1
        return mid if sums[mid] > target else -1

3、复杂度分析:

方法1:
时间复杂度:O(N^2),sum函数的复杂度是O(N)
空间复杂度:O(N)

方法2:
时间复杂度:O(N)
空间复杂度:O(1)
方法3:
时间复杂度:O(NlogN)
空间复杂度:O(N)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值