题目
给定一个含有 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 <= 10^9
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^5进阶:
如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-size-subarray-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
做过很多滑窗题目后,滑窗是比较直接好想的办法,当和小于target时扩张右边界,当不小于target后,更新最小的长度,并尽可能收缩左边界直至和再次小于target。
题目的进阶内容比较奇怪,实现O(n)算法后,再实现一个nlogn的。子数组求和当然更常见的做法是前缀和,那么对于一个区间[i,j],因为要求使preSum[j]-preSum[i]>=target成立的最大下标i,转变一下就是求最大的下标i使得preSum[i]<=preSum[j]-target。对于这道题,前缀和数组是严格递增的,因此可以使用二分求满足条件的下标i。
代码
前缀和+二分
class Solution {public int minSubArrayLen(int target, int[] nums) {int n = nums.length;int[] pre = new int[n + 1];for (int i = 1; i <= n; i++) {pre[i] = pre[i - 1] + nums[i - 1];}int ans = n + 1;for (int i = 1; i <= n; i++) {// 找最大的下标k使得pre[k]<=pre[i] - targetint k = bearch(pre, pre[i] - target);// 如果返回-1,说明对于下标j,[0,j]中不存在一个下标i使得sum[i,j]>=targetif (k >= 0) {// k是区间左边界,i-1为区间右边界,因此区间长度为i-kans = Math.min(ans, i - k);}}return ans == n + 1 ? 0 : ans;}// 找出nums中最后一个不大于k的数,返回其下标,如果不存在,返回-1private int bearch(int[] nums, int k) {int l = 0;int r = nums.length - 1;while (l < r) {int mid = l + (r - l + 1) / 2;if (nums[mid] > k) {r = mid - 1;} else {l = mid;}}if (nums[l] <= k) {return l;}return -1;}}
滑窗
class Solution {public int minSubArrayLen(int target, int[] nums) {int n = nums.length;int left = 0;int right = 0;int sum = 0;int ans = n + 1;while (right < n) {sum += nums[right];while (sum >= target) {ans = Math.min(ans, right - left + 1);sum -= nums[left];left++;}right++;}return ans == n + 1 ? 0 : ans;}}
