剑指 Offer II 008. 和大于等于 target 的最短子数组(209. 长度最小的子数组)

题目:

 

 

 

思路:

【1】利用队列+单次遍历整个数组的方式来处理(这种就是模仿滑动窗口的思维)

首先要了解到本质:
因为需要知道 nums 中是否有 连续子数组 (这个要求很重点,是做窗口的前提)
能够使元素的汇总sum >= target 
所以可以利用窗口的思维

而这个窗口,你可以理解为算是另一个存储元素的地方
因为你可以采用链表或者队列来做窗口也行
又由于对nums不会进行变更,采用双指针来作为边界,作为窗口也行

因为 target = 20 , nums = [1,2,3,4,5,15,10,21];
//用双指针做示例left = right = 0;
如果一开始的话
sum += nums[right++]; 当right = 5的时候nums[5] = 15,
此时 sum = 30 > 20 ,所以此时长度首先为right - left + 1 = 6,
这时候就要从左边吐数据了,先吐1,然后29>20,最小长度记为5
再吐2,以此类推
再吐3,以此类推
直到sum < target 或者 left == right

 

【2】滑动窗口的做法

代码展示:

滑动窗口的做法:

//时间4 ms击败8.46%
//内存48.7 MB击败62.20%
//这种算是通用的吧,利用二分缩小窗口大小,但是本质上还是会存在比较多无用的操作
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int lo = 1, hi = nums.length, min = 0;
        while (lo <= hi) {
            int mid = (lo + hi) >> 1;
            if (windowExist(mid, nums, s)) {
                hi = mid - 1;//找到就缩小窗口的大小
                min = mid;//如果找到就记录下来
            } else
                lo = mid + 1;//没找到就扩大窗口的大小
        }
        return min;
    }

    //size窗口的大小
    private boolean windowExist(int size, int[] nums, int s) {
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            if (i >= size)
                sum -= nums[i - size];
            sum += nums[i];
            if (sum >= s)
                return true;
        }
        return false;
    }
}

 

利用队列+单次遍历整个数组的方式来处理的方式:

//时间6 ms击败6.84%
//内存51.8 MB击败5.1%
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        LinkedList<Integer> queue = new LinkedList<>();
        int sum = 0 , res = Integer.MAX_VALUE;
        for (int x : nums){
            sum += x;
            queue.add(x);

            while (sum >= target){
                res = Math.min(res,queue.size());
                sum -= queue.pollFirst();
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}

//进一步采用双指针的方式替代队列(明显空间复杂度下降了,而且在这个过程中不用统计队列的大小)
//时间1 ms击败100%
//内存41.4 MB击败43.27%
class Solution {
    public int minSubArrayLen(int target, int[] nums) {
        int sum = 0 , left = 0 , res = Integer.MAX_VALUE;
        for (int i = 0 ; i < nums.length; i++){
            sum += nums[i];

            while (sum >= target){
                res = Math.min(res,i-left+1);
                sum -= nums[left++];
            }
        }
        return res == Integer.MAX_VALUE ? 0 : res;
    }
}

 

posted @ 2023-03-06 12:10  忧愁的chafry  阅读(19)  评论(0编辑  收藏  举报