剑指Offer专项突破版(8)—— 和大于等于 target 的最短子数组

题目

剑指 Offer II 008. 和大于等于 target 的最短子数组
在这里插入图片描述

思路

本题使用双指针的思路

一个子数组可以用两个指针表示:

  • left:指向子数组最左边的下标
  • right:指向子数组最右边的下标

暴力法

这道题的本质还是检查所有子数组,找出其和大于等于target,且长度最小的那个

如果单纯地遍历所有子数组,再对每个子数组求和,其时间复杂度为O(n ^ 3)

当然可以进行优化,比如预先处理出前缀和数组,这样对每个子数组求和这步操作就从O(n)降为O(1),总体时间复杂度降为O(n ^ 2)

双指针

我们来看看双指针解法:

一开始left = 0,right = 0,其代表的子数组中只有一个元素,sum = nums[0]

然后检查sum和target的关系:

  • sum < target:说明以left开头的子数组,如果以right结尾,其和不满足条件,下一步要扩大right,看扩大后是否能满足条件,并同步更新sum
  • sum >= target:说明以left开头的子数组,如果以right记为,是满足条件的:

    • 如果right再往右,也满足条件,但就不是长度最小的那个子数组了,因为其长度都大于[left,right]这个子数组,因此后面的子数组没有必要再去检查
    • 如果right往左,就不满足条件,可以排除(之前已经排除过了)
    • 因此我们等于是检查了所有以left为开始的子数组
  • 下一步:需要将left++。从sum减去nums[left],继续检查sum 和 target的关系,也就是检查下一个left

    • 这里为什么right不用重置为left,从[left,left]开始检查以left开始的每个子数组?
    • 答案是不用检查
    • 因为上一次循环从left-1开始(上一次循环的left),直到遇到right,sum才大于等于target,也就是说:从left-1一直到right-1,sum都小于target
    • 而本次循环left比上一次大1,从left到right-1的元素更少了,其和只可能更少,因此一样是小于target。而小于target的部分我们可以忽略掉,因为不满足条件,直接从right开始
    • 因此right指针不用回退

示例

以target = 7, nums = [5,1,4,3]为例:

  1. left,right一开始都指向5,sum = 5,小于7,right++,变为1
  1. 此时数组中包含两个数字5,1。sum = 6,任然小于7,right++,变为2
  1. 此时数组包含3个数字5,1,4。sum = 10,大于7,因此记录答案,长度为3
  1. 左移left,此时sum = 10 - 5 = 5,小于7,停止左移left,继续右移right
  1. 这样不断移动right,left,直到right大于nums.length为止

正确性证明

可以看出整个过程检查了以每个left为左端点的所有子数组,不会漏过任何一个子数组,因此计算出来的结果也是正确的

时间复杂度

由于left,right不会回退,最多从0到nums.length,因此时间复杂度为O(n)

可以看出,双指针法用O(N)的时间复杂度,就检查完了所有的子数组,就是因为其跳过了很多不可能是答案的子数组

代码

public int minSubArrayLen(int target, int[] nums) {
    int left = 0;
    int right = 0;
    int sum = 0;
    int minLen = Integer.MAX_VALUE;
    while (right < nums.length) {
        // 将nums[right]累加到sum
        sum += nums[right];
        if (sum < target) {
            right++;
            continue;
        }
         
         // 如果满足条件
         while (sum >= target) {
            // 计算出长度最小的那个
            minLen = Math.min(minLen,right - left + 1);
            sum -= nums[left];
            left++;
        }
        right++;
    }

    return minLen == Integer.MAX_VALUE ? 0 : minLen;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值