(1)滑动窗口算法介绍与练习:长度最小的子数组

滑动窗口算法介绍

所谓滑动窗口,即为同向双指针移动过程中形成的间隔区域,并且这两个指针在移动的过程中不会回退

对于滑动窗口的题目可以抽象为三个步骤:

  1. 定义窗口两端指针left和right
  2. 进入窗口
  3. 判断
  4. 离开窗口
  5. 循环2、3和4步

滑动窗口练习

长度最小的子数组

题目链接:209. 长度最小的子数组 - 力扣(LeetCode)

给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0 。

思路解析:

以下面的数组为例

cpp 复制代码
[ 2,3,1,2,4,3 ]

本题首先想到的就是暴力解法,暴力解法的思路很简单,第一层for循环遍历,选出区间左端点,第二层for循环遍历,选出区间右端点,最后一次遍历,将左右区间中的值全部相加求和,标记此时子数组的长度,如此往复直到找到子数组长度最小且子数组中元素之和>=target。根据这个暴力思路得出其时间复杂度为O()

cpp 复制代码
// 暴力解法
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = INT_MAX;
        for(int i = 0; i < nums.size(); i++)
        {
            for(int j = 0; j < nums.size(); j++)
            {
                int sum = 0;
                for(int k = i; k <= j; k++)
                {
                    sum += nums[k];
                    if(sum >= target)
                    {
                        len = min(len, j - i + 1);
                    }
                }
            }
        }
        return len == INT_MAX ? 0 : len;
    }
};

在暴力思路的基础上思考如何降低时间复杂度

单调性:因为题目给出了一个条件正整数的数组 ,在正整数范围内求和可以得到一个单调性的规律:加的数字越多和越大 ,所以如果第一次找到了一个和满足>=target,则该下标后的数值即可不需要遍历,例如下图中的4和3即可不需要遍历

正向性:因为在上一次的遍历过程中,已经找到了一组子数组和满足>=target,并且根据单调性可以得出right不需要再向后移动,接下来需要更新left寻找下一组此时left++,那么此时right是否需要回退到left的位置重新再来一次遍历呢?答案是不需要 ,因为left++,此时区间[left, right]是开始时(left=0)[left, right]区间的子区间,所以此时的区间(left=1)[left, right]中的元素和即为开始元素和-left=0时的值,所以可以得出,right在整个遍历过程中是不需要回退的,即保持正向移动,而left本身移动的方向为从左向右,所以left方向也是正向的

由以上两个性质,可以将暴力解法优化到O(N),即只需要一次遍历,而结合上面两种优化方式,可以得出滑动窗口算法

在本题中,滑动窗口的两端即为left和right,而滑动窗口中所维护的信息即为子数组之和sum,根据滑动窗口的基本解题步骤可以得出现在需要找到何时进窗口、判断以及何时出窗口

  1. 何时进窗口:本题中,因为窗口中维护的信息是sum,所以当开始求和时即为进窗口
  2. 判断:本题中,因为需要判断sum>=target,所以此为判断条件
  3. 何时出窗口:本题中,根据判断条件sum>=target可以得出,此条件成立时,证明已经得出了一个合理的结果,需要更新sum和子数组长度len,并让left向后移动(移动窗口,即出窗口),这一过程被称为更新结果(更新结果一般是贯穿在滑动窗口的解题步骤中,具体在哪一步由题目决定)

需要注意,本题需要求出子数组长度的最小值,所以 len不可以初始化为0,否则最后结果只会为0

具体步骤如下:

参考代码如下:

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len = INT_MAX, size = nums.size();
        int sum = 0;
        for(int left = 0, right = 0; right < size; right++)
        {
            // 进窗口
            sum += nums[right];
            // 判断
            while(sum >= target)
            {
                // 更新结果,出窗口
                len = min(len, right - left + 1);
                sum -= nums[left++];
            }
        }

        return len == INT_MAX ? 0 : len;
    }
};
相关推荐
inrichtt3 小时前
第八课时:SPI总线揭秘:四线全双工通信的奥
c语言·数据结构·stm32·单片机·嵌入式硬件
HPtiger4 小时前
数据结构——选择排序
数据结构·算法·排序算法
康实训4 小时前
老年评估仿真实训平台架构设计与老年人能力评估实训室教学落地实践
大数据·人工智能·算法·实训室·实训室建设
DongQiShanRen4 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CCblandy4 小时前
雪花 ID 四个边界实测:时钟回拨重号 500 个,前端 JSON.parse 直接丢精度
后端·python·算法·性能优化·架构
丑梨数据4 小时前
信创环境里跑文档工具:格式兼容与本地合规的几个坑
数据结构·人工智能·算法·决策树·排序算法
烂蜻蜓4 小时前
Pandas入门教程(四):Series数据结构——创建、索引与常用方法
数据结构·pandas
把我格子衫拿来4 小时前
VTK 9.5 与 Qt 6 联合开发:从嵌入三维窗口到可维护的桌面架构
c++·python·qt·vtk·三维可视化
无忧.芙桃5 小时前
C++语言原理与实践(十六):多态机制讲解
开发语言·c++·多态
erzong2316 小时前
Linux操作系统:线程概念与控制
linux·算法