力扣209(java&python)-长度最小的子数组(中等)

题目:

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

 示例 1:

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

输入:target = 4, nums = [1,4,4]
输出:1
示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0
 

提示:

1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
 

进阶:

如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/minimum-size-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

解题思路:

一、【前缀和+二分查找】

前缀和:sum[i]表示从nums[0]到nums[i-1]的元素和。本题中说明了每个元素都为正,所以前缀和一定是递增的。

  • 首先预处理出前缀和数组sum,前缀和数组下标从1开始,sum[i] = sum[i - 1] + nums[i - 1];
  • 假设s对应的前缀和为:s = sum[i+1]=sum[i] + nums[i],将nums[i]视为结果数组的右端点,现在问题转换为找结果数组的左端点,已知s和target,题目要求是要找到大于等于target的最小数组长度,于是可以转换成在前缀和数组下标[0,i]中内找到满足小于等于d = s - target的最大下标,这个最大下标就是结果数组的左端点。

注意:

代码中更新最短长度用的:i - left,为什么没有写成 i - left+ 1?

因为前缀和下标从1开始的,而left是从下标0开始的。故相减下来就是数组的长度。

 java代码:

 1 class Solution {
 2     public int minSubArrayLen(int target, int[] nums) {
 3         int n = nums.length, ans = Integer.MAX_VALUE;
 4         int[] sum = new int[n + 1];
 5         //计算前缀和
 6         for (int i = 1; i <= n; i++){
 7             sum[i] = sum[i - 1] + nums[i - 1];
 8         }
 9         for (int i = 1; i <= n; i++){
10             int s = sum[i], d = s - target;
11             int left = 0, right = i;
12             while (left < right){
13                 int mid = left + (right - left + 1) / 2;
14                 //不够d,需要往右移
15                 if (sum[mid] <= d){
16                     left = mid;
17                 }else{
18                     //超过了d,往左移
19                     right = mid - 1;
20                 }
21             }
22             //循环结束:left == right,找到小于等于d的最大下标,更新最小长度
23             if (sum[left] <= d){
24                 ans = Math.min(ans, i - left);
25             }
26         }
27         return ans == Integer.MAX_VALUE ? 0 : ans;
28     }
29 }

 二、【滑动窗口】--双指针

滑动窗口需要考虑的三点:

1.窗口内是什么?

2.怎么移动窗口的起始位置?

3.怎么移动窗口的结束位置?

然后本题就是:

1.窗口内就是 满足其和 ≥ s 的长度最小的 连续 子数组。

2.窗口的起始位置如何移动:如果当前窗口的值大于s了,窗口就要向前移动了(也就是该缩小了)。

3.窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

关键部分图解:

来源:代码随想录

复杂度:

主要是看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)。 

java代码:

 1 class Solution {
 2     public int minSubArrayLen(int target, int[] nums) {
 3         int n = nums.length, ans = Integer.MAX_VALUE;
 4         int left = 0, sublen = 0, sum = 0;
 5         for (int right = 0; right < n; right++){
 6             sum += nums[right];
 7             while (sum >= target){
 8                 //获取当前数组长度
 9                 sublen = right - left + 1;
10                 ans = Math.min(ans, sublen);
11                 sum -= nums[left++];
12             }
13         }
14         return ans == Integer.MAX_VALUE ? 0 : ans;
15     }
16 }

 python3代码:

 1 class Solution:
 2     def minSubArrayLen(self, target: int, nums: List[int]) -> int:
 3         left, ans, sum, sublen = 0, float("inf"), 0, 0
 4         for right in range(len(nums)):
 5             sum += nums[right]
 6             while sum >= target:
 7                 sublen = right - left + 1
 8                 ans = min(sublen, ans)
 9                 sum -= nums[left]
10                 left += 1
11         return 0 if ans == float("inf") else ans

posted on 2022-11-28 15:52  我不想一直当菜鸟  阅读(47)  评论(0编辑  收藏  举报