【算法】Array-长度最小的子数组

150 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

题目

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

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

思路

使用滑动窗口, 本质上也是一个双指针方法.

  • 前置思考: 必然要遍历整个数组, 才能找到最小的

  • 补充条件: 超出的数组必须连续, 考虑滑动窗口方法.

  • 问题关键: 两个指针的移动, start指针指窗口头, end指针指窗口尾, 如何移动两个指针?

  • end指针移动的逻辑: 由于必然要遍历一遍数组, 滑动窗口也只能在数组内部移动, 所以让end指针一直往数组末尾走, 不会回头, 当end指针走到尾, 整个数组就遍历完。

        while(end<nums.length){
            // end指针一直往数组尾走, 每走一步就要同步窗口内元素总和
            sum += nums[end++];
            ...
        }
    
  • start指针移动的逻辑:

    • 当end指针一直往前走, 带着滑动窗口也一直扩大, 接下来会有两种情况, end一直走到数组尾, sum也没超过target, 那么就根据要求结束方法, 返回0;

      // 直接走这儿
      return minlen == Integer.MAX_VALUE ? 0 : minlen;
      
    • 另一种情况是end指针走到数组中间某处, 此时sum>target, 那么就应当记录下此时窗口长度end-start.

    注意: 这里之所以是 end-start , 而不是 (end - start + 1) 是因为我们在求完sum后让end++了, 所以现在end指向滑动窗口尾部位置的后一位, 而不是滑动窗口的尾部位置

    1.drawio.png

    • 这时我们满足了第一个要求: 连续子数组之和大于target. 但此时还不一定满足第二个需求: 长度最小, 也可以理解为滑动窗口的长度最小. 所以此时应当考虑前移start指针.

    • start前移的活动规律是: 它不断前移, 每次移动都重新记录sum值, 而start每次前移, 就说明滑动窗口缩小了, 所以长度也要重新记录. 最终只会出现一种情况: 每次前移都减去一部分sum值, sum值不断减少至sum<target, 此时不满足第一个要求了, start停止前移.

    • 所以start前移也应当在一个循环里, 停止循环的条件是: sum<target

    while(sum >= target){
        minlen = Math.min(minlen, end-start);
        sum -= nums[start++];
    }
    
  • 两个while循环的组合问题:

    • 代码里存在两个循环, 第一个不断让end前移, 第二个不断让start前移.
    • 在我们刚才的分析中, 让end前移, 以满足题目的第一个要求——"连续子数组", 而满足第二个要求——"子数组长度最小", 是建立在满足第一个要求基础上才能满足的,才能让start前移, 执行第二个循环。
    • 这种关系决定了,两个循环是嵌套的,控制end的循环在外,控制start的循环在内。完整的代码看下文:

代码

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int start = 0;
        int end = 0;
        int sum = 0;
        int minlen = Integer.MAX_VALUE;
        while(end<nums.length){
            sum+=nums[end++];
            while(sum>=target){
                minlen = Math.min(minlen, end-start);
                sum-=nums[start++];
            }
        }
        return minlen==Integer.MAX_VALUE?0:minlen;
    }
}

总结

  • 滑动窗口方法是双指针方法的变体,难度有上升,不会做非常合理!