算法练习-长度最小的子数组(思路+流程图+代码)

难度参考

难度:简单

分类:数组

难度与分类由我所参与的培训课程提供,但需要注意的是,难度与分类仅供参考。以下内容均为个人笔记,旨在督促自己认真学习。

题目

给定一个含有个正整数的数组和一个正整数s,找出该数组中满足其和≥s的长度最小的连续子数组,并返回其长度。如果不存在符合条件的子数组,返回0。

示例1:

输入:s=7,

nums=[2,3,1,2,4,3]

输出:2

解释:子数组[4,3]是该条件下的长度最小的子数组。

思路

暴力做法

使用暴力法解决这道题的思路是遍历所有可能的连续子数组,计算它们的和,并找到满足条件的最小子数组长度。以下是暴力法的详细题解:

  1. 初始化一些变量,包括最小长度 minLength 初始为正无穷大。

  2. 使用两层循环,外层循环以每个元素为起点,内层循环遍历从该起点开始的子数组。外层循环变量 start 从0开始,内层循环变量 endstart 开始。

  3. 在内层循环中,计算子数组的和 sum,即从 nums[start]nums[end] 的元素的累加和。

  4. 如果 sum 大于或等于目标值 s,说明当前子数组的和满足条件,可以记录下当前子数组的长度 end - start + 1

  5. 在外层循环中,不断更新 minLength,即记录当前满足条件的子数组的最小长度。

  6. 继续外层循环,直到遍历完整个数组。

  7. 最后,如果 minLength 没有被更新过,说明没有满足条件的子数组,返回0;否则,返回 minLength

这个算法的核心思想是遍历所有可能的子数组,计算它们的和并比较长度,找到最小长度的满足条件的子数组。由于使用了两层循环,时间复杂度是O(n^2),其中n是数组的长度。这个算法虽然不如滑动窗口法高效,但是可以解决问题。

暴力做法不再提供示例与梳理,感觉可以直接看代码。

滑动窗口

可以使用滑动窗口的方法解决这个问题。滑动窗口是维护一个连续子数组的常用技巧,通过左指针和右指针来移动窗口,根据窗口内元素的和来调整窗口的大小。具体步骤如下:

  1. 初始化左指针 left 为0,右指针 right 为0,以及窗口内元素的和 sum 为0。

  2. 使用右指针 right 向右遍历数组,不断将元素添加到窗口内,并更新 sum

  3. sum 大于等于给定的正整数 s 时,记录当前窗口的长度 right - left + 1

  4. 缩小窗口,即左指针 left 向右移动,同时从 sum 中减去左边界的元素,直到 sum 小于 s

  5. 重复步骤2到4,直到遍历完整个数组。

  6. 在整个过程中,不断更新最小子数组的长度,最终得到最小长度。

通过滑动窗口找到最小长度的连续子数组,时间复杂度为O(n),其中n是数组的长度。

示例

理解滑动窗口算法可能有点抽象,让我尝试以更简单的方式解释它。

简单解释:

滑动窗口算法就像你在一堆连续的数字中寻找一个连续的子集,这个子集的和大于等于给定的值s,而且这个子集的长度要尽可能小。

首先,你从数组的开头找到一个子集,看它的和是否满足条件。如果和小于s,你就继续扩大子集,添加更多的数字。如果和大于等于s,你记录下这个子集的长度。

接下来,你缩小子集的范围,从左边开始移除数字,然后再检查新的子集是否满足条件。如果满足,你再次记录子集的长度,然后继续缩小范围。

你不断地重复这个过程,直到遍历完整个数组。最终,你会找到一个满足条件的子集,它的长度是最小的。

这就是滑动窗口算法的核心思想:不断调整子集的范围,以找到满足条件的最小子集。

让我们使用一个示例来说明滑动窗口算法的工作方式:

示例:

假设有一个数组 nums,其内容如下:

nums = [2, 3, 1, 2, 4, 3]

我们的目标是找到一个连续的子数组,该子数组的和大于等于7,并且长度尽可能小。

步骤1:初始化窗口

我们从左到右遍历数组,初始化左指针 left 和右指针 right,以及窗口内的和 sum

left = 0, right = 0, sum = 0

步骤2:扩展窗口

我们开始扩展窗口,将右指针 right 向右移动,逐个添加元素,并更新 sum 的值。我们的目标是找到一个子数组,其和大于等于7。

left = 0, right = 0, sum = 2 
left = 0, right = 1, sum = 5 
left = 0, right = 2, sum = 6 
left = 0, right = 3, sum = 8

在这个过程中,当 sum 大于等于7时,我们记录下当前窗口的长度(right - left + 1),并且这是我们找到的目前最小的长度。

步骤3:缩小窗口

接下来,我们需要缩小窗口,即将左指针 left 向右移动,同时从 sum 中减去左边界的元素。我们不断缩小窗口,以尝试找到更小的子数组。

left = 1, right = 3, sum = 7

在这一步,我们找到了一个和为7的子数组,长度为3,这是目前找到的最小长度。

步骤4:继续寻找

然后,我们继续向右移动右指针 right,并尝试寻找更小的子数组。

left = 1, right = 4, sum = 11

在这一步,我们找到了一个和为11的子数组,长度为4。

步骤5:缩小窗口

接着,我们再次缩小窗口,继续寻找更小的子数组。

left = 2, right = 4, sum = 9

在这一步,我们找到了一个和为9的子数组,长度为3。

步骤6:继续寻找

我们继续向右移动右指针 right,寻找更小的子数组。

left = 2, right = 5, sum = 12

在这一步,我们找到了一个和为12的子数组,长度为4。

步骤7:缩小窗口

最后,我们再次缩小窗口。

left = 3, right = 5, sum = 10

在这一步,我们找到了一个和为10的子数组,长度为3。

图示:

2+3+1+2=8>7(找出该数组中满足其和≥s的长度),第一次更新滑动窗口长度。

尝试缩小窗口(移动左指针),发现3+1+2=6<7。

因此,继续寻找(移动右指针),调整窗口(1+2+4>7),第二次更新滑动窗口长度。

同理,在尝试缩小窗口(移动左指针【先】)与继续寻找(移动右指针【后】)之后,调整窗口(1+2+4>7),第三次更新滑动窗口长度。

尝试缩小窗口(移动左指针),发现4+3>=7,第四次更新滑动窗口长度。

尝试缩小窗口(移动左指针),发现3<7, 继续寻找(移动右指针), 右指针 j > 数组长度,结束循环。我们就得到了所需要的窗口长度(即该数组中满足其和≥s的长度最小的连续子数组的长度)。

结果:

在整个过程中,我们不断调整窗口的大小,以找到和大于等于7的最小子数组。最终,我们找到了一个和为7的子数组,长度为2。这是我们要找的答案。

所以,滑动窗口算法的结果是2,表示最小连续子数组的长度为2,即子数组 [4, 3]

梳理

滑动窗口算法之所以能够实现找到满足条件的最小连续子数组,是因为它巧妙地利用了窗口的概念,通过不断调整窗口的大小和位置,来搜索满足条件的最小子数组。以下是为什么这个算法能够实现的原因:

  1. 窗口的左右边界移动: 算法使用两个指针,一个左指针和一个右指针,它们分别表示当前窗口的左边界和右边界。通过不断移动这两个指针,算法模拟了不同窗口的情况。

  2. 窗口内元素和的计算: 算法维护一个变量 sum,用于记录当前窗口内元素的和。随着右指针的移动,不断将新元素添加到窗口内,并更新 sum。这使得算法能够动态地计算窗口内元素的和。

  3. 根据和的大小调整窗口: 在每一步中,算法检查 sum 是否满足给定的条件(例如,是否大于等于s)。如果满足条件,算法会记录当前窗口的长度,然后尝试缩小窗口,即移动左指针。如果不满足条件,算法会继续扩大窗口,即移动右指针。

  4. 不断更新最小长度: 算法在整个过程中不断记录最小的子数组长度。每当找到一个满足条件的子数组时,它会与之前记录的最小长度比较,然后更新最小长度。这确保了算法找到的是最小的满足条件的子数组。

  5. 遍历整个数组: 算法通过不断移动右指针,遍历整个数组,以寻找满足条件的子数组。因为算法考虑了数组中的每个元素,所以它能够找到所有可能的子数组,从中选择最小长度的子数组。

总结来说,滑动窗口算法通过动态地维护一个窗口,根据窗口内元素和的大小来调整窗口的位置和大小,从而找到满足条件的最小子数组。它的核心思想是不断地搜索可能的子数组,然后选择最小长度的子数组作为答案。这个算法的时间复杂度为O(n),因为每个元素最多被访问两次(一次添加到窗口,一次从窗口移除),其中n是数组的长度。

代码

暴力做法

#include <iostream>
#include <vector>
#include <climits> // 包含 <climits> 头文件以引入 INT_MAX

using namespace std;

// 定义一个函数,找到满足和≥s的最短连续子数组的长度(暴力法)
int minSubArrayLen(int s, vector<int>& nums) {
    int n = nums.size();  // 获取数组的大小
    int minLength = INT_MAX;  // 初始化最小长度为最大整数

    for (int start = 0; start < n; start++) {  // 以每个元素为起点
        int sum = 0;  // 定义当前子数组的和

        for (int end = start; end < n; end++) {  // 从起点开始遍历子数组
            sum += nums[end];  // 向子数组内添加元素

            if (sum >= s) {  // 如果子数组的和满足条件
                minLength = min(minLength, end - start + 1);  // 更新最小长度
                break;  // 退出内层循环,继续下一个起点
            }
        }
    }

    // 如果minLength没有被更新,说明没有满足条件的子数组,返回0;否则返回最小长度
    return minLength == INT_MAX ? 0 : minLength;
}

int main() {
    int s = 7;  // 给定的正整数s
    vector<int> nums = {2, 3, 1, 2, 4, 3};  // 给定的正整数数组

    // 调用函数找到满足条件的最短连续子数组的长度(暴力法)
    int result = minSubArrayLen(s, nums);

    cout << "最小连续子数组的长度为:" << result << endl;  // 输出结果

    return 0;
}

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

空间复杂度:O(1)

滑动窗口

#include <iostream>
#include <vector>
#include <climits> // 包含 <climits> 头文件以引入 INT_MAX

using namespace std;

// 定义一个函数,找到满足和≥s的最短连续子数组的长度
int minSubArrayLen(int s, vector<int>& nums) {
    int n = nums.size();  // 获取数组的大小
    int minLength = INT_MAX;  // 初始化最小长度为最大整数
    int left = 0;  // 定义左指针
    int sum = 0;  // 定义当前窗口内元素的和

    for (int right = 0; right < n; right++) {  // 使用右指针遍历数组
        sum += nums[right];  // 向窗口内添加一个元素

        while (sum >= s) {  // 当窗口内元素和大于等于s时
            minLength = min(minLength, right - left + 1);  // 更新最小长度
            sum -= nums[left];  // 缩小窗口,左指针向右移动
            left++;  // 左指针向右移动
        }
    }

    // 如果minLength没有被更新,说明没有满足条件的子数组,返回0;否则返回最小长度
    return minLength == INT_MAX ? 0 : minLength;
}

int main() {
    int s = 7;  // 给定的正整数s
    vector<int> nums = {2, 3, 1, 2, 4, 3};  // 给定的正整数数组

    // 调用函数找到满足条件的最短连续子数组的长度
    int result = minSubArrayLen(s, nums);

    cout << "最小连续子数组的长度为:" << result << endl;  // 输出结果

    return 0;
}

时间复杂度:O(n)

空间复杂度:O(1)

打卡

暴力做法打卡

滑动窗口打卡

相关推荐
小阮的学习笔记6 小时前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
爱分享的淘金达人2 天前
2025年山东省考报名流程图解
java·考研·spring·eclipse·tomcat·流程图
烟雨国度6 天前
Spring MVC 完整生命周期和异常处理流程图
spring·mvc·流程图
冰淇淋噢!7 天前
一般公司流程图详情版
流程图
半块菠萝10 天前
html简易流程图
css·html·流程图
寰梦14 天前
上传Gitee仓库流程图
gitee·流程图
技术路上的苦行僧15 天前
Spring源码解析(35)之Spring全体系源码流程图
java·spring·流程图·1024程序员节
烟雨国度15 天前
阿里云用STS上传oss的完整程序执行流程图 和前端需要哪些参数uniapp
javascript·阿里云·uni-app·流程图·1024程序员节
懒大王952715 天前
vue使用 jsplumb 生成流程图
java·前端·流程图
emperinter17 天前
sankey.top - 桑基图/桑吉图/流程图/能量流/物料流/能量分析
macos·ios·音视频·流程图·能源·apple vision pro·web app