基础算法---滑动窗口

文章目录

  • 什么是滑动窗口
  • 1.长度最小的子数组
  • 2.无重复字符的最长子串
  • 3.最大连续1的个数
  • 4.将x减到0的最小操作数
  • 5.最小覆盖子串
  • 总结

在这里插入图片描述

什么是滑动窗口

滑动窗口(Sliding Window)是一种在计算机科学中用于解决各种子数组或子字符串问题的技术。滑动窗口技术通过维护一个固定大小的窗口在数组或字符串上移动,从而使得可以在较短的时间内解决一些复杂的问题。这种方法在处理一系列数据时特别高效。滑动窗口(Sliding Window)是一种在计算机科学中用于解决各种子数组或子字符串问题的技术。滑动窗口技术通过维护一个固定大小的窗口在数组或字符串上移动,从而使得可以在较短的时间内解决一些复杂的问题。这种方法在处理一系列数据时特别高效。
在这里插入图片描述
滑动窗口其实可以理解为一个左指针和一个右指针共同维护的一个区间,然后一起移动,这个区间的长度可能是不变的,也可能是变化的。
滑动窗口的几个基本步骤:
1.进窗口
2.判断是否出窗口
3.更新结果
接下来我们用几道题来演示滑动窗口这个算法。

1.长度最小的子数组

题目链接
题目:

在这里插入图片描述

样例输入和输出:

在这里插入图片描述

这道题的意思就是让我们求数组中和为target或者大于target的长度最小的子数组。首先我们来看看示例1:
在这里插入图片描述

从下图可以直观的看见最后一个子数组是最短的,所以这里输出是2,理解了题意接下来我们来想想怎么解。
解法一:暴力解法
暴力解法很容易想到,我们用两层循环将所有情况全部遍历一遍,然后用一个长度len来记录每次循环的最小的结果,然后最后遍历完了之后返回结果。
解法二:滑动窗口
暴力枚举的优化算法
在这里插入图片描述
我们看1,当我们遍历到1的情况的时候,按照暴力解法,先left指针后移,然后将right指针移到left指针重新遍历一遍即可,但是我们想一想有没有必要将right指针移到left的位置重新遍历,首先left指针向后移动一个位置之后只会出现两种情况,第一种情况:满足条件大于等于target,大于等于target而且长度还比前一个长度短,所以这里可以直接更新结果,没必要将right移到left重新遍历,来看第二种情况,,就是left向后移动以后不满足条件,不满足条件了,那将right移动到left重新遍历到未移动的位置都不满足,所以这里更不用重新遍历,所以综上两种情况,都不需要将right移到left的位置重新遍历。所以这里我们满足情况只需要将left++,right不需要移到left重新遍历,每次left移动之后还是要判断一下是否满足条件,满足条件则更新结果。
所以这里很快就出来了:
步骤就是1.进窗口 2.判断是否出窗口,更新结果
代码展示:

class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
    int left = 0,right=0;
    int sum=0,len=INT_MAX;//len取最大,因为len最后求的是min
    while (right < nums.size())
    {
        //进窗口
        sum+=nums[right];
        //循环判断
        while(sum>=target)
        {
            len=min(len,right-left+1);//更新结果
            sum-=nums[left++];//出窗口
        }
        right++;
    }
    return len==INT_MAX?0:len;
}
};

2.无重复字符的最长子串

题目链接
题目:

样例输入和输出:

在这里插入图片描述

这道题题目意思很简单,,就是让我们求无重复字符的子串,并且还是最长的。

解法一:
暴力解法,暴力解法很简单,把这个字符串给遍历一遍,然后将所有的子串的情况全部求出来,每次求出一种判断一下,这里判断我们就用hash表,看看子串中字符的种类,如果每个种类只有一种说明这个子串是符合要求的,如果hash表中有一个字符是有多个的,说明这个子串是不满足的。最后返回满足的最长的子串即可。
解法二:滑动窗口+hash
在这里插入图片描述

首先我们还是需要用到hash表,用来记录区间内的字符的种类,但是暴力解法是逐个遍历,优化的滑动窗口当我们遍历到第一种情况的时候right指针继续向后移动。
在这里插入图片描述
这里这个滑动窗口已经不满足条件了所以这里我们应该让left++,left++之后right还有没有必要向前移动,很显然是没必要的,因为和上一道题相似,这里无非就是两种情况:
1.left移动之后这个区间不成立,这个区间都不成立了right还从前往后遍历是不是多余?
2.left移动之后这个区间成立了,这个区间成立,但是和前一个区间是相同长度的区间,所以向前遍历的长度都没有这个区间长,所以更不需要向前遍历。
综上right只需要向后移动,不需要向前移动。

细节:每次我们入窗口的时候都需要判断一下hash表中的字符种类的个数是否只有一个,如果有多个的话则移动left然后将hash表中的元素进行–。直到减到1个为止。然后再更新结果。

代码展示:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        //创建一个hash数组用来记录重复字母的个数
        int hash[128]={0};
        int left=0,right=0;
        int len=0;
        int n=s.size();
        while(right<n)
        {
            hash[s[right]]++;
            while(hash[s[right]]>1)
            {
                hash[s[left]]--;
                left++;
            }
            len=max(len,right-left+1);
            right++;
        }
        return len;
    }
};

3.最大连续1的个数

题目链接
题目:

在这里插入图片描述

样例输入和输出:

在这里插入图片描述

这道题大致的意思就是我们需要找到连续的1的个数最大的子数组,并且我们可以将0翻转成1。
在这里插入图片描述
对于第一个例子来说,由于我们可以将0翻转成1,所以第一个区间是最长的,第二个区间和第三个区间是一样长的,但是比第一个区间长,所以我们可以返回最长的区间长度。

解法一:暴力解法
暴力解法也只需要暴力枚举,枚举出所有的情况,但是我们这道题需要正难则反,因为如果我们真的翻转每一个零的话,这道题是很难的,这道题要我们求最长的连续的1的区间,我们可以转换为求最长的连续1的区间并且如果这个区间涵盖0的话,零的个数不能超过k个,如果这个区间的0的个数不超过k个那么这个区间就是合法的。转换了之后就很好做了,没枚举一个把这个数放进hash表,然后判断其零的个数,如果零的个数是合法的,那么这个区间就成立,则更新最长结果。
解法二:滑动窗口+hash表
这道题转换了之后就和上一道一个样了,我们只需要用一个两个空间的hash表来记录这里面1和0的个数即可,每次入完窗口之后,都判断一下这个窗口是否合法,也就是判断一下这个窗口内的0的个数是否大于k个了,如果没有大于k个,则继续,更新结果。

代码展示:

class Solution {
public:
int longestOnes(vector<int>& nums, int k) 
{
    //定义左右指针
    int left = 0, right = 0;
    //定义长度并且定义长度
    int len = 0, n = nums.size();
    //定义1的个数和0的个数
    int arr[2]={0};
    while (right < n)
    {
        //入窗口
        arr[nums[right]]++;
        //判断窗口内的零的个数,如果零大于k的时候就left++,并且出窗口
        while(arr[0]>k)
        {
            arr[nums[left]]--;
            left++;
        }
        //判断完了之后更新长度
        len = max(len, right - left + 1);
        //更新right
        right++;
    }
    //返回最大长度
    return len;
}
};

4.将x减到0的最小操作数

题目链接
题目:

在这里插入图片描述

样例输入和输出:

在这里插入图片描述

这道题的大致意思我们每次只能选最左边或者最右边的数,然后每次选了这个数之后,这个数就被删除了,不能选这个数,最后选了数我们要选出最少的数使得能将x减为零的长度。大致就是这个意思。

这道题要是我们直接做的话,会相当难,我们可以换个角度来看这个道题,这道题其实就是让我们求左右两个不连续的区间的和为x,我们用第一个例子来看:
在这里插入图片描述
这样转换之后这道题的难度瞬间降低了一个层次。
解法一:
暴力枚举,暴力枚举每种情况,这里我们初始化需要将len初始化为-1,如果最后len等于-1,则返回len,如果最后len不是-1,则返回的是nums的长度-len,暴力枚举每种情况,然后求出最长的那个。

解法二:
滑动窗口,这里步骤我们就不讲了,我们讲一下细节的,出窗口的条件,当我们用一个sum记录和的时候,发现这个和大于的时候就开始出窗口。
代码展示;

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum=0;
        for(auto e:nums)sum+=e;
        int target=sum-x;
        //细节问题
        if(target<0)return -1;
        int ret=-1;
        for(int left=0,right=0,tmp=0;right<nums.size();right++)
        {
            //进窗口
            tmp+=nums[right];
            //判断出窗口
            while(tmp>target)
            {
                tmp-=nums[left++];
            }
            if(tmp==target)
            {
                ret=max(ret,right-left+1);
            }
        }
        if(ret==-1)return ret;
        else
        return nums.size()-ret;
    }
};

5.最小覆盖子串

题目链接
题目

在这里插入图片描述

样例输入和输出;

在这里插入图片描述

这道题题目意思也很简单,我们需要找到一个子串,这个子串满足在s中可以有重复的字符,但是不能少字符,意思就是s中的子串的字符的种类大于等于t中的字符种类,字符的个数也是大于等于t中的字符个数。

解法一:暴力枚举+hash表
首先我们暴力枚举出每种情况,然后再利用两个hash表,先将t中的字符存在hash表中,然后在暴力枚举的过程中,我们每入一个窗口,都需要判断其窗口内的字符种类,然后暴力枚举出满足的情况之后取子串,返回其子串。
解法二:滑动窗口+hash表
入窗口什么的还是和前面讲的一样,但是唯一有一个区别是这里是满足条件擦开始出窗口,
在这里插入图片描述
这里蓝色窗口的区间满足了条件,我们开始出窗口,出窗口有两种可能,就是还满足条件,还有一种是不满足条件,如果还满足条件则更新最小的结果,如果不满足了,继续入窗口,所以这里我们更新结果的地方应该在出窗口之前,这里我们需要用一个变量记录有效字符的个数,首先我们先用一个变量kinds来记录t中的字符的种类,然后再用count记录区间中有效字符的个数,在记录窗口中有效字符的个数的时候,我们只需要判断一下这个字符是否存在于t中即可,如果存在于t中则为有效字符,并且这里如果t中a有1个,s中a有很多个,这里我们只记录一次有效数据。

代码展示:

class Solution {
public:
string minWindow(string s, string t)
{
    int hash1[128] = { 0 }, hash2[128] = { 0 };
    int left = 0, right = 0;
    int kinds = 0;//统计有效字符的个数
    for (auto e : t)
    {
        if (hash1[e] == 0)kinds++;//统计表中的字符
        hash1[e]++;
    }
    int count = 0;
    int minlen = INT_MAX;int begin = -1;
    while (right < s.size())
    {
        hash2[s[right]]++;
        if (hash2[s[right]] == hash1[s[right]])
        {
            count++;
        }
        while (count == kinds)
        {
            if (right - left + 1 < minlen)
            {
                minlen = right - left + 1;
                begin = left;
            }
            if (hash1[s[left]] == hash2[s[left]])
            {
                count--;
            }
            hash2[s[left]]--;
            left++;
        }
        right++;
    }
    if (begin == -1)return "";
    else return s.substr(begin, minlen);
}
};

总结

滑动窗口算法是一种高效且灵活的技术,它可以显著减少解决某些问题的时间复杂度。在处理数组和字符串相关的问题时,滑动窗口尤其有效,它通过动态调整窗口的大小来满足特定的条件,避免了不必要的重复计算。

在本文中,我们详细讨论了滑动窗口的基本概念和应用场景,并通过具体的例子展示了如何使用滑动窗口解决无重复字符的最长子串问题。通过这些示例,我们可以看到滑动窗口的强大之处,以及在实际编程中如何灵活应用这项技术。

希望这篇文章能帮助你更好地理解滑动窗口算法,并在以后的算法学习和实践中熟练应用这项技术。如果你有任何疑问或新的想法,欢迎在评论区留言,我们一起讨论交流。感谢阅读!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/736076.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在 Mac 上恢复已删除的文件夹

“嗨&#xff0c;我刚刚运行了重复文件查找器应用程序 Gemini 来扫描我的 Mac 以清除重复文件。它找到了很多重复的文件和文件夹&#xff0c;只需单击一下&#xff0c;它就可以帮助我删除重复的文件/文件夹。但我认为它可能会删除一些有用的重复文件。我打开垃圾箱&#xff0c;…

主数据驱动的数据治理:技术解析与实践探索

数字化转型行业小伙伴可以加入我的星球&#xff0c;初衷成为各位数字化转型参考库&#xff0c;星球内容每周更新 个人工作经验资料全部放在这里&#xff0c;包含数据治理、数据要素、数据质量、数据安全、元数据、主数据、企业架构、DCMM、DSMM、CDGA、CDGP等各种数据相关材料 …

AOP应用之系统操作日志

本文演示下如何使用AOP&#xff0c;去实现系统操作日志功能。 实现步骤 引入AOP包 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId><version>2.6.6</version></de…

AI大眼萌探索 AI 新世界:Ollama 使用指南【1】

在人工智能的浪潮中&#xff0c;Ollama 的出现无疑为 Windows 用户带来了一场革命。这款工具平台以其开创性的功能&#xff0c;简化了 AI 模型的开发与应用&#xff0c;让每一位爱好者都能轻松驾驭 AI 的强大力量。大家好&#xff0c;我是AI大眼萌&#xff0c;今天我们将带大家…

颠覆多跳事实验证!Causal Walk 前门调整技术引领去偏新纪元

Causal Walk: Debiasing Multi-Hop Fact Verifcation with Front-Door Adjustment 论文地址: Causal Walk: Debiasing Multi-Hop Fact Verification with Front-Door Adjustment| Proceedings of the AAAI Conference on Artificial Intelligencehttps://ojs.aaai.org/index.p…

基于Vue-cli脚手架搭建项目使用ElementUI组件

项目结构 node_modules 项目依赖的外部组件文件放在此处,例如vue public index.html是对外提供的唯一的html文件 src assets 存放静态文件 例如图片 css js等文件 components 里面存放的是组件 App.vue是组件 main.js是项目配置文件 package.json存放的是项目依赖的…

C# Onnx Yolov5 水果识别,人员识别,物品识别 人工智能

目录 先上效果 来电废话&#xff0c;但实用 网络成功案例实践易失败的原因 万物检测涉及技术 下载合集 关键代码 全部代码 实操vs2022安装关键 YOLO V5核心库编译 编写自己识别软件 更新相关依赖 标注字库文件 测试效果 名词解释YOLO 名词解释ONNX 源码 直播教…

基于Java的火车订票管理系统【附源码】

火车订票管理登录 摘要&#xff1a;随着我国铁路交通的不断发展&#xff0c;简单的窗口售票模式已经不能满足方便人们出行的目的。采用先进的网络技术开发出方便快捷的火车票订票系统是现代客运业务发展的必然需求。本次设计的火车票订票系统通过访问主页&#xff0c;可以实现…

Linux PXE高效批量装机

部署PXE远程安装服务 在大规模的 Linux 应用环境中&#xff0c;如 Web 群集、分布式计算等&#xff0c;服务器往往并不配备光驱设备&#xff0c;在这种情况下&#xff0c;如何为数十乃至上百台服务器裸机快速安装系统呢?传统的USB光驱、移动硬盘等安装方法显然已经难以满足需…

VC++支持断点续下或续传的功能

VC使用多线程和Socket实现断点续下 一、断点续下的基本原理&#xff1a; 1.断点续传的理解可以分为两部分&#xff1a;一部分是断点&#xff0c;一部分是续传。断点的由来是在下载过程中&#xff0c;将一个下载文件分成了多个部分&#xff0c;同时进行多个部分一起的下载&…

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现(六十四)

Python武器库开发-武器库篇之ThinkPHP 5.0.23-RCE 漏洞复现&#xff08;六十四&#xff09; 漏洞环境搭建 这里我们使用Kali虚拟机安装docker并搭建vulhub靶场来进行ThinkPHP漏洞环境的安装&#xff0c;我们进入 ThinkPHP漏洞环境&#xff0c;可以 cd ThinkPHP&#xff0c;然…

每日一题——Python代码实现PAT乙级1048 数字加密(举一反三+思想解读+逐步优化)五千字好文

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 初次尝试 再次尝试 代码点评 代码结构 时间复杂度 空间复杂度 优化建议 我要更强…

【Linux】IP协议、以太网帧格式

目录 网络层IP协议协议头格式网段划分分类划分法特殊的 IP 地址IP 地址的数量限制私有 IP 地址和公有 IP 地址路由路由表生成算法 数据链路层以太网以太网帧格式认识 MAC 地址ARP协议ARP数据报格式 ARP 协议的工作流程ARP欺骗 DNShosts 文件域名的层级关系域名服务器分类域名解…

day3-xss漏洞(米斯特web渗透测试)

day3-xss漏洞&#xff08;米斯特web渗透测试&#xff09; XSSXss种类三种反射型1.反射型xss2.存储型xss3.DOM型xss XSS Xss有一部分是前端的有一部分不是前端的&#xff0c;我们来看一下&#xff0c;昨天的HTML注入修复方法应灵活使用。 HTML注入是注入一段HTML&#xff0c;那…

mysql中in参数过多该如何优化

优化方式概述 未优化前 SELECT * FROM rb_product rb where sku in(1022044,1009786)方案2示例 public static void main(String[] args) {//往list里面设置3000个值List<String> list new ArrayList<>();for (int i 0; i < 3000; i) {list.add(""…

8.DELL R730服务器对RAID5进行扩容

如果服务器的空间不足了&#xff0c;如何进行扩容&#xff1f;我基本上按照如何重新配置虚拟磁盘或添加其他硬盘来进行操作。我的机器上已经有三块硬盘了&#xff0c;组了Raid5&#xff0c;现在再添加一块硬盘。 先把要添加的硬盘插入服务器&#xff0c;无论是在IDRAC还是管理…

Phi-3 模型手机部署教程(微软发布的可与GPT-3.5媲美的小模型)

前面几篇博文&#xff0c;老牛同学和大家一起在个人电脑部署了Qwen2、GLM4、Llama3、ChatTTS和Stable Diffusion等 LLM 大模型&#xff0c;也通过 API 和 WebUI 的方式完成了体验。 但是这些大模型因为部署在个人电脑本地&#xff0c;不能够随时携带。如果能在手机上部署大模型…

python判断语句

目录 布尔类型和比较运算符if语句的基本格式if else 语句if elif else 语句判断语句的嵌套 布尔类型和比较运算符 1、布尔类型 bool布尔类型只有两个结果&#xff1a;真或假 布尔类型的字面量&#xff1a; True 表示真&#xff08;是、肯定&#xff09; False 表示假&#x…

Python+Pytest+Yaml+Request+Allure框架源代码之(一)common公共方法封装

common模块&#xff1a; get_path.py&#xff1a;获取路径方法 # -*- coding: UTF-8 -*- import os# 项目根目录 BASE_DIR os.path.dirname(os.path.dirname(os.path.abspath(__file__)))# 配置文件目录 CONFIG_DIR os.path.join(BASE_DIR,config)# 测试用例文件目录 TESTCA…

【数据结构导论】自考笔试题:伪代码练习题汇总 1

目录 一、开源项目推荐 二、线性表的基本运算在单链表上的实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;插入 p 指向的新结点的操作 &#xff08;3&#xff09;删除 *p 节点 三、循环链表 &#xff08;1&#xff09;在单链表中 &#xff08;2&…