【优选算法】—— 滑动窗口类问题

这篇具有很好参考价值的文章主要介绍了【优选算法】—— 滑动窗口类问题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本期,我给大家带来的是关于滑动窗口类算法的介绍,并通过具体的题目帮助大家思考理解。

滑动窗口算法,算法,算法 

 文章来源地址https://www.toymoban.com/news/detail-783053.html

目录

(一)基本概念

(二)题目讲解

1、难度:medium

1️⃣长度最小的子数组

2️⃣找到字符串中所有字⺟异位词

2、难度:hard

1️⃣最⼩覆盖⼦串

2️⃣串联所有单词的⼦串

总结


(一)基本概念

滑动窗口算法(Sliding Window Algorithm)是一种常见的算法技巧,用于解决一些数组或字符串相关的问题。该算法基于窗口的概念,在一个固定大小的窗口内移动,并根据具体问题进行适当的调整和计算。

下面将详细的介绍滑动窗口算法的工作原理和应用场景:

工作原理

  • 窗口大小:滑动窗口算法通过设定一个窗口的大小来解决问题。窗口通常是一个连续的子数组或子字符串。
  • 初始化窗口:初始化窗口的起始位置,并根据问题需求设定窗口的大小。
  • 移动窗口:通过移动窗口的起始位置,不断调整窗口的大小和位置,以找到满足问题条件的解。
  • 更新解:根据窗口的移动和调整,更新问题的解,并记录或返回所需的结果。

应用场景:

  • 最小/最大子数组/子字符串:寻找给定数组或字符串中满足特定条件的最小或最大的子数组或子字符串。
  • 字符串匹配:在一个字符串中寻找另一个字符串的出现或满足特定条件的子串。
  • 滑动窗口和哈希表结合:通过使用哈希表来优化滑动窗口算法,提高效率。
  • 优化窗口大小:根据问题的特性,调整窗口大小以寻找最佳解。

滑动窗口算法的步骤通常如下:

  1. 初始化窗口的起始位置和结束位置,使其满足问题的要求。
  2. 进入循环,不断移动窗口的起始位置和结束位置,直到窗口滑动到数组或字符串的末尾。
  3. 在每一次循环中,检查窗口内的元素是否满足问题的要求。如果满足条件,则更新解或执行其他操作。如果不满足条件,则继续移动窗口。
  4. 在移动窗口时,要更新窗口内的元素和相应的数据结构,以确保窗口的正确性。
  5. 重复步骤2到步骤4,直到遍历完整个数组或字符串,返回解或所需的结果。

(二)题目讲解

接下来,我们通过几道题目让大家具体的感受一下。(题目由易到难

1、难度:medium

1️⃣长度最小的子数组

题⽬链接:209. 长度最小的子数组

 【题⽬描述】

滑动窗口算法,算法,算法

【解法】(滑动窗⼝

算法思路:
由于此问题分析的对象是「⼀段连续的区间」,因此可以考虑「滑动窗」的思想来解决这道题。
让滑动窗⼝满⾜:从 i 位置开始,窗⼝内所有元素的和⼩于target (那么当窗内元素之和
第⼀次⼤于等于⽬标值的时候,就是 i 位置开始,满⾜条件的最⼩⻓度)。


做法:将右端元素划⼊窗⼝中,统计出此时窗⼝内元素的和:

  • 如果窗⼝内元素之和⼤于等于 target :更新结果,并且将左端元素划出去的同时继续判断是否满⾜条件并更新结果(因为左端元素可能很⼩,划出去之后依旧满⾜条件)
  • 如果窗⼝内元素之和不满⾜条件: right++ ,另下⼀个元素进⼊窗⼝。
     

【原理解释】

很多题解以及帖⼦只给你说怎么做,没给你解释为什么这么做。为何滑动窗⼝可以解决问题,并且时间复杂度更低?


▪ 这个窗⼝寻找的是:以当前窗⼝最左侧元素(记为 left1 )为基准,符合条件的情况。也
就是在这道题中,从 left1 开始,满⾜区间和 sum >= target 时的最右侧(记为
right1 )能到哪⾥。


▪ 我们既然已经找到从 left1 开始的最优的区间,那么就可以⼤胆舍去 left1 。但是如
果此时重新开始统计第⼆个元素( left2 )往后的和,势必会有⼤量重复的计算(因为我们在求第⼀段区间的时候,已经算出很多元素的和了,这些和是可以在计算下次区间和的时候⽤上的)。


▪ 此时, rigth1 的作⽤就体现出来了,我们只需将 left1 这个值从 sum 中剔除。从
right1 这个元素开始,往后找满⾜ left2 元素的区间(此时 right1 也有可能是满⾜的,因为 left1 可能很⼩。 sum 剔除掉 left1 之后,依旧满⾜⼤于等于target )。这样我们就能省掉⼤量重复的计算。


▪ 这样我们不仅能解决问题,⽽且效率也会⼤⼤提升
 

【算法实现】

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

【结果展示】

滑动窗口算法,算法,算法

 【性能分析】

 具体的性能分析如下:

  • 时间复杂度:因为每个元素最多被遍历两次(进窗口和出窗口),时间复杂度是线性的。所以时间复杂度为O(n),其中n是数组nums的长度。
  • 空间复杂度:代码中只使用了几个额外的变量,不随输入规模变化,所以空间复杂度为O(1)

 

2️⃣找到字符串中所有字⺟异位词

题⽬链接:438. 找到字符串中所有字母异位词

 

【题⽬描述】

滑动窗口算法,算法,算法

【解法】(滑动窗⼝+ 哈希表)


算法思路:

  1. 因为字符串 p 的异位词的⻓度⼀定与字符串 p 的⻓度相同,所以我们可以在字符串 s 中构造⼀个⻓度为与字符串 p 的⻓度相同的滑动窗⼝,并在滑动中维护窗⼝中每种字⺟的数量;
  2. 当窗⼝中每种字⺟的数量与字符串 p 中每种字⺟的数量相同时,则说明当前窗⼝为字符串 p 的异位词;
  3.  因此可以⽤两个⼤⼩为 26 的数组来模拟哈希表,⼀个来保存 s 中的⼦串每个字符出现的个数,另⼀个来保存 p 中每⼀个字符出现的个数。这样就能判断两个串是否是异位词。

【算法实现】

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        vector<int> ret;
        int hash1[26] = { 0 }; // 统计字符串 p 中每个字符出现的个数
        for(auto ch : p) 
            hash1[ch - 'a']++;
        int hash2[26] = { 0 }; // 统计窗⼝⾥⾯的每⼀个字符出现的个数
        int m = p.size();
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            // 进窗⼝ + 维护 count
            if(++hash2[in - 'a'] <= hash1[in - 'a']) 
                count++;
            if(right - left + 1 > m) // 判断
            {
                char out = s[left++];
                // 出窗⼝ + 维护 count
                if(hash2[out - 'a']-- <= hash1[out - 'a']) 
                    count--;
            }
            // 更新结果
            if(count == m) 
                ret.push_back(left);
        }
        return ret;
    }
};

【结果展示】

滑动窗口算法,算法,算法

 【性能分析】

具体的性能分析如下:

  • 时间复杂度:代码中使用了双指针来遍历字符串s,所以时间复杂度为O(n),其中n是字符串s的长度。
  • 空间复杂度:代码中使用了两个大小为26的哈希数组,所以空间复杂度为O(1)

2、难度:hard

1️⃣最⼩覆盖⼦串

链接如下:76. 最小覆盖子串

【题⽬描述】

 滑动窗口算法,算法,算法

【解法】 (滑动窗⼝+哈希表

算法思路:
◦ 研究对象是连续的区间,因此可以尝试使⽤滑动窗⼝的思想来解决。
◦ 如何判断当前窗⼝内的所有字符是符合要求的呢?

  • 我们可以使⽤两个哈希表,其中⼀个将⽬标串的信息统计起来,另⼀个哈希表动态的维护窗⼝内字符串的信息。
  • 当动态哈希表中包含⽬标串中所有的字符,并且对应的个数都不⼩于⽬标串的哈希表中各个字符的个数,那么当前的窗⼝就是⼀种可⾏的⽅案

【算法流程】

a. 定义两个全局的哈希表: 1 号哈希表 hash1 ⽤来记录⼦串的信息, 2 号哈希表 hash2 ⽤来记录⽬标串 t 的信息;


b.  实现⼀个接⼝函数,判断当前窗⼝是否满⾜要求:
    i. 遍历两个哈希表中对应位置的元素:
        • 如果 t 中某个字符的数量⼤于窗⼝中字符的数量,也就是 2 号哈希表某个位置⼤于1 号哈希表。说明不匹配,返回 false ;
        • 如果全都匹配,返回 true

主函数中:
a. 先将 t 的信息放⼊ 2 号哈希表中;
b. 初始化⼀些变量:左右指针: left = 0,right = 0 ;⽬标⼦串的⻓度: len =
INT_MAX ;⽬标⼦串的起始位置: retleft ;(通过⽬标⼦串的起始位置和⻓度,我们就
能找到结果)
c. 当 right ⼩于字符串 s 的⻓度时,⼀直下列循环:

  • i. 将当前遍历到的元素扔进 1 号哈希表中;
  • ii. 检测当前窗⼝是否满⾜条件:

• 如果满⾜条件:
        ◦ 判断当前窗⼝是否变⼩。如果变⼩:更新⻓度 len ,以及字符串的起始位置
retleft ;
        ◦ 判断完毕后,将左侧元素滑出窗⼝,顺便更新 1 号哈希表;
        ◦ 重复上⾯两个过程,直到窗⼝不满⾜条件;

  • iii. right++ ,遍历下⼀个元素;

d. 判断 len 的⻓度是否等于 INT_MAX :

  • i. 如果相等,说明没有匹配,返回空串;
  • ii. 如果不想等,说明匹配,返回 s 中从 retleft 位置往后 len ⻓度的字符串。

【算法实现】

class Solution {
public:
    string minWindow(string s, string t) {
        int hash1[128] = { 0 }; // 统计字符串 t 中每⼀个字符的频次
        int kinds = 0; // 统计有效字符有多少种
        for(auto ch : t)
        if(hash1[ch]++ == 0) kinds++;
        int hash2[128] = { 0 }; // 统计窗⼝内每个字符的频次
        int minlen = INT_MAX, begin = -1;
        for(int left = 0, right = 0, count = 0; right < s.size(); right++)
        {
            char in = s[right];
            if(++hash2[in] == hash1[in]) count++; // 进窗⼝ + 维护 count
            while(count == kinds) // 判断条件
            {
                if(right - left + 1 < minlen) // 更新结果
                {
                    minlen = right - left + 1;
                    begin = left;
                }
                char out = s[left++];
                if(hash2[out]-- == hash1[out]) count--; // 出窗⼝ + 维护 count
            }
        }
        if(begin == -1) 
            return "";
        else 
            return s.substr(begin, minlen);
    }
};

【结果展示】

滑动窗口算法,算法,算法

 

【性能分析】

具体的性能分析如下:

  • 时间复杂度:算法的时间复杂度为O(n),n为字符串s的长度。代码使用哈希表记录频次信息,实现了线性时间复杂度。
  • 空间复杂度:哈希表hash1和hash2的存储空间为128个元素的数组,因此空间复杂度为O(1)


2️⃣串联所有单词的⼦串

链接如下:30. 串联所有单词的子串

【题⽬描述】

滑动窗口算法,算法,算法

滑动窗口算法,算法,算法 

 【解法】

算法思路:


如果我们把每⼀个单词看成⼀个⼀个字⺟,问题就变成了找到「字符串中所有的字⺟异位词」。⽆
⾮就是之前处理的对象是⼀个⼀个的字符,我们这⾥处理的对象是⼀个⼀个的单词。
 

因此,这道题就留给大家自己做了!!


总结

以上就是本期关于滑动窗口算法的主要内容讲解了。感谢大家的观看与支持!!!

滑动窗口算法,算法,算法

 

到了这里,关于【优选算法】—— 滑动窗口类问题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【算法】基础算法002之滑动窗口(二)

    👀 樊梓慕: 个人主页  🎥 个人专栏: 《C语言》 《数据结构》 《蓝桥杯试题》 《LeetCode刷题笔记》 《实训项目》 《C++》 《Linux》 《算法》 🌝 每一个不曾起舞的日子,都是对生命的辜负 目录 前言  5.水果成篮(medium)  6.找到字符串中所有字母异位词 7.串联所有单词的

    2024年02月20日
    浏览(48)
  • LeetCode算法小抄--滑动窗口算法

    ⚠申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计6244字,阅读大概需要3分钟 🌈更多学习内容, 欢迎👏关注👀文末我的个人微信公众号:不懂开发的程序猿 个人网站:https://jerry-jy.co/ 滑动窗口算法 思路 1、我们在字符串 S 中使用双指针中的

    2023年04月09日
    浏览(40)
  • 算法专题二:滑动窗口

    长度最小的子数组 思路一: 思路二: 无重复字符的最长子串 思路一: 最大连续1的个数 开头位0 并且k为0算是一个特殊的情况这样的情况有一些考虑就走不出去前面的0而忽略到后面数组自带1,走不到这些1数据导致记录连续1的个数出问题! 将x减小到0的最小操作数 水果成篮

    2024年02月02日
    浏览(50)
  • 「算法」滑动窗口

    算法需要多刷题积累经验,所以我行文重心在于分析解题思路,理论知识部分会相对简略一些 滑动窗口属于双指针,这两个指针是 同向前行 ,它们所夹的区间就称为“窗口” 啥时候用滑动窗口? 题目涉及到“子序列”、“子数组”、“子串”等概念,要你求和它们相关的

    2024年02月20日
    浏览(42)
  • 【滑动窗口、矩阵】算法例题

    目录  三、滑动窗口 30. 长度最小的子数组 ② 31. 无重复字符的最长子串 ② 32. 串联所有单词的子串 ③ 33. 最小覆盖子串 ③ 四、矩阵 34. 有效的数独 ② 35. 螺旋矩阵 ② 36. 旋转图像 ② 37. 矩阵置零 ② 38. 生命游戏 ②  给定一个含有  n   个正整数的数组和一个正整数 

    2024年04月14日
    浏览(69)
  • 算法笔记--滑动窗口

    力扣209.长度最小子数组 https://leetcode.cn/problems/minimum-size-subarray-sum/ 在这道题中要注意的不仅仅是滑动窗口的问题,更重要的问题是在循环控制中,不恰当的语法使用会导致这道题出现很严重的问题,这导致我做这道题做了很多天,真的很崩溃。 先来看一下循环的控制问题,

    2024年02月11日
    浏览(45)
  • 【算法小课堂】滑动窗口

    滑动窗口本质是双指针算法的一种演变 本质上就是同向双指针,窗口的范围就是[left,right) 滑动窗口大致可以分为两类 窗口大小不变的 窗口大小变化的 滑动窗口遇到一些验证重复性的问题的时候可以用哈希表来优化 三步走: 窗口的形成初期 进窗口 开始遍历数组,right一直

    2024年02月07日
    浏览(41)
  • 【算法专题】滑动窗口

    题目链接 - Leetcode -209.长度最小的子数组 Leetcode -209.长度最小的子数组 题目:给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组[numsl, numsl + 1, …, numsr - 1, numsr] ,并返回其长度。 如果不存在符合条件的子数组,返

    2024年02月05日
    浏览(45)
  • 【算法|数组】滑动窗口

    给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度**。**如果不存在符合条件的子数组,返回 0 。 示例 1: 示例 2: 示例 3: 暴力解法 这种做法可以很容易想到,可是谁

    2024年02月12日
    浏览(39)
  • 算法专题整理:滑动窗口

    视频课程:同向双指针 滑动窗口【基础算法精讲 01】_哔哩哔哩_bilibili 滑动窗口也可以理解为双指针法的一种。 滑动窗口的核心在于 连续 !如果需要得到的结果是 满足某种较为明显条件 的 连续 的 子数组 ,可以考虑使用滑动窗口来做。 通常情况下,滑动窗口是 枚举右端

    2024年02月12日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包