import java.util.*; /** * NC343 和大于等于K的最短子数组 * @author d3y1 */ public class Solution { /** * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可 * * * @param nums int整型ArrayList * @param k int整型 * @return int整型 */ public int shortestSubarray (ArrayList<Integer> nums, int k) { // return solution1(nums, k); // return solution11(nums, k); return solution2(nums, k); // return solution3(nums, k); } /** * 二分+前缀和 * * num值不可为负数, 以保证使用二分法的前提(单调性) * * @param nums 正整数数组! * @param k * @return */ private int solution1(ArrayList<Integer> nums, int k){ int n = nums.size(); // 前缀和 long[] preSum = new long[n+1]; for(int i=1; i<=n; i++){ preSum[i] = preSum[i-1]+nums.get(i-1); } int result = Integer.MAX_VALUE; // 窗口大小 int left = 1; int right = n; int mid; // 二分 while(left <= right){ mid = left+(right-left)/2; if(check(preSum, k, mid)){ result = Math.min(result, mid); right = mid-1; }else{ left = mid+1; } } return result==Integer.MAX_VALUE?-1:result; } /** * 校验gap大小窗口是否满足条件(preSum[j]-preSum[i] >= k) * @param preSum * @param target * @param gap * @return */ private boolean check(long[] preSum, int target, int gap){ for(int i=0,j=i+gap; j<preSum.length; i++,j++){ if(preSum[j]-preSum[i] >= target){ return true; } } return false; } /** * 二分+前缀和 * * num值不可为负数, 以保证使用二分法的前提(单调性) * * @param nums 正整数数组! * @param k * @return */ private int solution11(ArrayList<Integer> nums, int k){ int n = nums.size(); // 前缀和 long[] preSum = new long[n+1]; for(int i=1; i<=n; i++){ preSum[i] = preSum[i-1]+nums.get(i-1); } int result = Integer.MAX_VALUE; // 前缀数组索引 int left,right,mid; for(int i=0; i<=n; i++){ left = i; right = n; // 二分 while(left <= right){ mid = left+(right-left)/2; // left最终指向第一个满足条件(preSum[left]-preSum[i]>=k)的位置(从左往右) if(preSum[mid]-preSum[i] < k){ left = mid+1; }else{ right = mid-1; } } // 当前未找到满足条件位置 后续更不可能满足条件(直接结束循环) if(left > n){ break; } result = Math.min(result, left-i); } return result==Integer.MAX_VALUE?-1:result; } /** * 双指针(毛毛虫) * * num值可为负数 * * 相似 -> NC41 最长无重复子数组 [nowcoder] * 相似 -> NC170 最长不含重复字符的子字符串 [nowcoder] * 相似 -> NC356 至多包含K种字符的子串 [nowcoder] * 相似 -> NC387 找到字符串中的异位词 [nowcoder] * * @param nums 正整数数组! * @param k * @return */ private int solution2(ArrayList<Integer> nums, int k){ int n = nums.size(); int result = Integer.MAX_VALUE; // 双指针(毛毛虫) int left=0; int right=0; int sum = 0; int numL,numR; while(right < n){ numR = nums.get(right++); if(numR >= k){ return 1; } sum += numR; while(left<=right && sum>=k){ result = Math.min(result, right-left); numL = nums.get(left++); sum -= numL; } } return result==Integer.MAX_VALUE?-1:result; } /** * 单调栈(双端队列Deque): 单调增 * * num值可为负数 * * @param nums 整数数组! * @param k * @return */ private int solution3(ArrayList<Integer> nums, int k){ int n = nums.size(); // 前缀和 long[] preSum = new long[n+1]; for(int i=1; i<=n; i++){ preSum[i] = preSum[i-1]+nums.get(i-1); } int result = Integer.MAX_VALUE; // 双端队列 Deque<Integer> queue = new ArrayDeque<>(); for(int i=0; i<=n; i++){ long curSum = preSum[i]; // 判断 是否满足条件(>=k) while(!queue.isEmpty() && curSum-preSum[queue.peekFirst()]>=k){ result = Math.min(result, i-queue.pollFirst()); } // 单调栈(单调增) while(!queue.isEmpty() && preSum[queue.peekLast()]>=curSum){ queue.pollLast(); } queue.offerLast(i); } return result==Integer.MAX_VALUE?-1:result; } }