题解 | #长度最小的连续子数组-(二分 + 前缀和)-(双指针 - 窗口)#

长度最小的连续子数组

http://www.nowcoder.com/practice/10dd5f8c5d984aa3bd69788d86aaef23

描述

题目描述

给定我们一个数组,然后一个总和,让我们找到一个区间,满足区间的和大于等于这个总和,输出区间的长度,如果没有的话,我们可以直接输出00

样例解释

样例输入

[1,2,4,4,1,1,1],9

这个满足总和相加大于等于99的最短区间,我们可以选择2,4,44,4,12,4,4也可以选择4,4,1,他们长度一样所以最后输出

3

题解

解法一:双指针维护窗口

实现思路

首先我们有两个指针,分别指向我们这个窗口的左右边界,我们的右指针每次向外扩展我们的这个窗口,然后每次,我们判断如果左指针可以收缩,我们一直让左指针收缩,这样我们最后得到的就是满足条件的了,最后我们每次求取出我们的最小值即可

图解代码

假设我们现在左指针和右指针的位置已经是这样了,然后我们判断,如果右指针向前移动一位,这个区间内是否总和还是大于等我们给定的那个值

20220113174704

这里我们发现可以移动右指针,我们移动右指针

20220113174804

然后我们可以发现,我们现在的最小值是33

然后我们继续移动左指针

20220113174838

然后我们可以发现,我们可以继续移动右指针

20220113174905

最小值还是33,之后的也是以此类推,然后我们就可以得到我们的最小值

代码实现

class Solution {
public:
    int minSubarray(vector<int>& nums, int target) {
        int l = 0, r = 0, sum = 0, length = INT_MAX;
//         左边界,有边界,总和,长度
        while (r < nums.size()) {
//             如果没遍历完我们的数组
            sum += nums[r];
//             我们先扩展
            while (sum - nums[l] >= target) sum -= nums[l++];
//             收缩我们的窗口
            if (sum >= target) length = min(length, r - l + 1);
//             求取最小值
            ++r;
        }
        return length == INT_MAX ? 0 : length;
//         判断是否成立
    }
};

时空复杂度分析

时间复杂度: O(n)O(n)

理由如下:我们左右指针无交叉,我们只是简单的扫描了一遍数组,所以我们的复杂度只是遍历一次数组

空间复杂度: O(1)O(1)

理由如下:我们只是引用了常数级别的变量空间

解法二:二分 + 前缀和

解题思路

      首先我们可以从题目中的要求,要求一个子数组的总和要大于某一个值,那么我们快速求取某一段区间的区间和,可以考虑使用我们的前缀和数组,然后我们可以发现,这整个数组都是正整数,那么我们就可以保证我们的前缀和数组是单调递增的,这时候我们要对单调性有一个敏感度,看到单调性,我们可以考虑二分,考虑到了二分之后,我们想怎么去二分,有什么办法

      这里因为我们已经判断出来了我们的前缀和数组是单调递增的,所以这里我们考虑一下在前缀和数组上做一下文章,我们想一下如何利用前缀和数组的单调性来分,我们可以很容易的想到,假设我们对前缀和数组的第ii位进行考虑,想要满足题目中条件一段区间和大于一个值,我们可以将我们当前的前缀和数组的第ii位加上这个值,然后我们在我们的前缀和数组中去寻找是否有大于等于这个值的出现,因为我们的前缀和数组是单调递增的,所以我们是可以直接整个数组去判断的,如果可以找到大于我们前缀和数组的第ii位加上我们的目标值的位置,我们就知道了我们子数组的结束位置,然后我们不断更新结束位置到起始位置的长度,就是我们最后子数组的大小

      还有另一种思路,留给读者朋友去思考实现,我们可以二分我们的答案长度,假设我们取一个中间值作为我们最后子数组的长度,然后我们去验证是否可行,如果可以,那么我们说明比这个长度小或者等于这个长度的也或许可以,如果验证过后发现不可以,说明我们当前取到的长度小了,我们要扩大我们的长度

      以上就是常见的两种二分的思路,一个是正向的二分,另一个是我们可知我们的答案满足某种单调性的条件,我们去二分我们的答案,然后验证更改范围

代码实现

class Solution {
public:
    int minSubarray(vector<int>& nums, int target) {
        int n = nums.size(), minn = INT_MAX;
        vector<int> sum(n + 1, 0);
//         前缀和数组
        for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + nums[i - 1];
//         求出前缀和
        for (int i = 0; i <= n; i++) {
            int tar = target + sum[i];
//             对每一位的初始值进行改变
            int pos = -1;
//             位置
            if (lower_bound(sum.begin(), sum.end(), tar) != sum.end()) pos = lower_bound(sum.begin(), sum.end(), tar) - sum.begin();
//             如果可以找到第一个不小于tar的值,就是我们这个区间的和大于target,那么我们更新位置坐标
            if (pos != -1) minn = min(minn, pos - i);
//             判断最小的坐标
        }
        return minn == INT_MAX ? 0 : minn;
//         返回我们的最小值
    }
};

时空复杂度分析

时间复杂度: O(nlogn)O(nlogn)

理由如下:首先我们每次二分的时间复杂度是O(logn)O(logn)的,然后我们遍历我们的整个长度为nn的数组,时间是O(n)O(n)的,那么我们最后的时间复杂度就是O(nlogn)O(nlogn)

空间复杂度: O(n)O(n)

理由如下:我们开辟了一个前缀和的数组,空间大小为我们原来数组的大小

机试题目题解 文章被收录于专栏

主要是机试题目的题目讲解和做法

全部评论

相关推荐

钱嘛数字而已:拖拉机被发明出来之后,就不需要农民了吗?农民还是需要的,但不需要这么多了,另外对农民的要求也变高了,需要会开拖拉机。
点赞 评论 收藏
分享
评论
2
1
分享

创作者周榜

更多
正在热议
更多
# 一张图晒出你司的标语 #
4295次浏览 75人参与
# AI面会问哪些问题? #
27838次浏览 553人参与
# 厦门银行科技岗值不值得投 #
8033次浏览 188人参与
# 你的实习产出是真实的还是包装的? #
20197次浏览 342人参与
# 找AI工作可以去哪些公司? #
9097次浏览 233人参与
# 春招至今,你的战绩如何? #
65238次浏览 582人参与
# 开放七大实习专项,百度暑期实习值得冲吗 #
15218次浏览 221人参与
# 从事AI岗需要掌握哪些技术栈? #
8932次浏览 304人参与
# 中国电信笔试 #
32001次浏览 292人参与
# 你做过最难的笔试是哪家公司 #
33504次浏览 231人参与
# 投递几十家公司,到现在0offer,大家都一样吗 #
340809次浏览 2174人参与
# 阿里笔试 #
178566次浏览 1316人参与
# 哪些公司真双非友好? #
69587次浏览 289人参与
# 机械人避雷的岗位/公司 #
62703次浏览 393人参与
# 第一份工作一定要去大厂吗 #
14611次浏览 122人参与
# 金三银四,你的春招进行到哪个阶段了? #
22077次浏览 280人参与
# 为了减少AI幻觉,你注入过哪些设定? #
26250次浏览 310人参与
# 沪漂/北漂你觉得哪个更苦? #
9843次浏览 193人参与
# 应届生第一份工资要多少合适 #
20683次浏览 86人参与
# HR最不可信的一句话是__ #
6238次浏览 114人参与
# AI时代,哪个岗位还有“活路” #
11535次浏览 345人参与
# 春招你拿到offer了吗 #
831221次浏览 9987人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务