题目来源:力扣(LeetCode)https://leetcode-cn.com/problems/minimum-size-subarray-sumpython
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中知足其和 ≥ s 的长度最小的连续子数组,并返回其长度。若是不存在符合条件的连续子数组,返回 0。数组
示例:bash
输入: s = 7, nums = [2,3,1,2,4,3] 输出: 2 解释: 子数组 [4,3] 是该条件下的长度最小的连续子数组。
进阶:微信
若是你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。app
思路:双指针 / 前缀和+二分查找spa
先看题目,题目说明,数组 nums 给定的元素都是正整数,同时给一个整数 s,求子数组和大于等于 s 的最小连续子数组,返回长度。不存在则返回 0。3d
这里先说下特殊状况,上面根据题意已说明,nums 中的元素都是正整数,那么若是数组全部元素和都小于 s 的话,那么子数组是必定不能知足要求的,直接返回 0 便可。指针
进阶 提示的内容部分中,要求先完成 O(n) 时间复杂度的解法。这就是咱们如今要说明的 双指针 思路的解法。code
具体的实现思路:blog
left
,right
,同时指向索引为 0 的初始位置;num_sum
存储数组和,用以与 s
比较;num_sum
值,当 num_sum
知足大于等于 s
的条件时,先记录此时的数组长度,而后尝试缩小数组的长度。num_sum
和 s
的值。循环直至指针到达末尾位置。双指针实现思路的过程,可见下图:
关于图中的参数
当 num_sum 大于等于 s 时表示知足条件。
s:题目所给的目标值
num_sum:子数组之和
left:左指针
right:右指针
ans:知足条件的子数组长度
具体的代码见 【代码实现 # 双指针】。
上面是双指针的思路,下面尝试使用 O(nlogn) 复杂度的方法:前缀和 + 二分查找。
关于前缀和,这里就不展开说明这个概念了。这里咱们用数组 num_sum
存储 nums
的前缀和。
num_sum[i]
:表示 nums[0]
到 nums[i-1]
的元素之和。
仍是由于有 【给定一个含有 n 个正整数的数组】 的前提,那么咱们存储前缀和的数组必定是递增升序的,这样也能保证二分查找的正确性。
当咱们获得存储前缀和的数组以后,咱们能够遍历数组经过二分查找获得一个索引 index
,使得 num_sum[index] - num_sum[i-1] >= s
,更新维护子数组的长度,(此时的长度为 index -(i-1))
具体的代码见 【代码实现 # 前缀和+二分查找】。
# 双指针 class Solution: def minSubArrayLen(self, s: int, nums: List[int]) -> int: # 先处理特殊状况,由于给定的数组都是正整数 # 若是数组中全部元素的和都小于 s # 那么子数组的和必定都小于 s,直接返回 0 if sum(nums) < s: return 0 # 定义双指针 left = 0 right = 0 length = len(nums) # 定义变量,存储元素和,用以跟 s 比较 num_sum = 0 ans = float('inf') # 遍历数组,移动指针 while right < length: num_sum += nums[right] # 知足条件时,尝试缩小子串的长度,寻找最小的连续子数组 while num_sum >= s: ans = min(ans, right - left + 1) num_sum -= nums[left] left += 1 right += 1 return ans # 前缀和+二分查找 class Solution: def minSubArrayLen(self, s: int, nums: List[int]) -> int: def bin_search(num_sum, left, right, target): while left < right: mid = (left + right) // 2 if num_sum[mid] < target: left = mid+1 else: right = mid # 这里要注意,有可能最后一位元素大于目标值 # 这种状况要考虑,若是不知足,也就是最后一位元素不大于目标值时,返回 -1,这种状况不更新计算长度。 return left if num_sum[left] >= target else -1 # 先处理特殊状况,当数组中的元素和小于 s, # 则表示数组中全部子数组的和都不可能大于 s # 这个时候,直接返回 0 if sum(nums) < s: return 0 ans = float('inf') length = len(nums) # 存储前缀和 num_sum = [0] for i in range(length): num_sum.append(num_sum[-1] + nums[i]) # 经过二分查找,找到符合条件的索引,计算长度 for i in range(1, length+1): target = s + num_sum[i-1] index = bin_search(num_sum, 1, length, target) if index != -1: ans = min(ans, index-(i-1)) return ans
双指针 | 实现结果
前缀和+二分查找 | 实现结果
题目中要求先实现 O(n) 时间复杂度的解法。这里使用双指针的方法,具体实现以下:
left, right
,定义变量 num_sum
存储子数组的和。num_sum
小于目标值 s
时,先移动右指针,维护更新 num_sum
。当 num_sum
知足条件也就是大于或等于 s
时,先记录此时子数组的长度。此时移动左指针尝试缩小数组,再次比较 num_sum
和 s
的值。循环判断直至 right
到达末尾位置。题目进阶说能够尝试 O(nlogn) 时间复杂度的解法,这里可联想到二分查找。一样根据第一项所说的前提,能够定义数组存储 nums
数组的元素前缀和。具体实现以下:
num_sum
存储数组的前缀和,num_sum[i]
表示 nums[0]
到 nums[i-1]
元素之和。index
使得 num_sum[index]-nums[i-1]
的和大于或等于 s
,记录此时的子数组长度(index-(i-1))。文章原创,若是以为写得好,欢迎关注点赞。微信公众号《书所集录》同步更新,一样欢迎关注点赞。