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

news2025/1/24 14:57:27

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.长度最小的子数组

滑动窗口类问题解题思路大纲:

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

3.最大连续1的个数Ⅲ

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


前言

本篇文章主要会讲解滑动窗口的解题思想,滑动窗口实际上就是利用双指针的基础思想,并且利用单调性进行解题的方法。

滑动窗口所用到的双指针是用来维护这个所谓的『 窗口』,所以这两个指针是『 同向』且『 不回退』的,这也就决定了滑动窗口解题的时间复杂度最多为O(2N) 即O(N),所以滑动窗口是一种非常优秀的算法思想。

那么滑动窗口思想具体的应用,以及如何分析判断是否适用滑动窗口解题呢?


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。

=========================================================================

GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟

=========================================================================


1.长度最小的子数组

209. 长度最小的子数组 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/minimum-size-subarray-sum/

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

如果依照暴力枚举的策略,解题思路大致如下:

利用双指针,一个指针固定,移动另一个指针,当两个指针中间的所有元素和大于等于目标值target时,记录此时的长度,然后循环往复,求长度最小值。

代码如下:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 记录结果
        int ret = INT_MAX;
        int n = nums.size();
        // 枚举出所有满⾜和⼤于等于 target 的⼦数组[start, end]
        // 由于是取到最⼩,因此枚举的过程中要尽量让数组的⻓度最⼩
        // 枚举开始位置
        for (int start = 0; start < n; start++)
        {
            int sum = 0; // 记录从这个位置开始的连续数组的和
            // 寻找结束位置
            for (int end = start; end < n; end++)
            {
                sum += nums[end]; // 将当前位置加上
                if (sum >= target) // 当这段区间内的和满⾜条件时
                {
                    // 更新结果,start 开头的最短区间已经找到
                    ret = min(ret, end - start + 1);
                    break;
                }
            }
        }
        // 返回最后结果
        return ret == INT_MAX ? 0 : ret;
    }
};

可其实这其中有太多可以忽略掉的枚举区间,我们来分析一下:

本题要求的是长度最小子数组,所以此时一定是要更新left的位置即可,这就达到了不回退的目的。

注意:要在移动left之前更新结果。

所以此类问题,我们可以将left和right中间的区域看作一块窗口,该窗口不断的向后移动,直到right超出数组为止。

在这一过程中:

  • right移动导致元素进入窗口的行为我们称为『 进入窗口』;
  • left移动导致元素离开窗口的行为我们称为『 离开窗口』;
  • 判断left和right维护的窗口是否满足题目条件我们称为『 判断』;
  • 满足题目条件时更新结果的行为我们称为『 更新结果』。

所以以上这些步骤就构成了『 滑动窗口』类问题的解题思路。

滑动窗口类问题解题思路大纲:

①left=0,right=0;

②进入窗口;

③判断;        

        离开窗口;

④更新结果;

其中更新结果取决于具体的题目要求,『 就题论题』。

比如本题就需要在『 离开窗口』之前『 更新结果』。

有了思路,画图独立完成代码,不要直接看博主的代码。

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

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

3. 无重复字符的最长子串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/longest-substring-without-repeating-characters/description/

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

其实滑动窗口类题目最重要的是弄清楚『 为什么』要使用滑动窗口,而不是『 怎样』适用滑动窗口。

像本题,你是如何分析出要使用滑动窗口的呢?

首先依据暴力枚举的策略,同样固定一个指针,移动另一指针,搭配哈希表得到最长字串:

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ret = 0; // 记录结果
        int n = s.length();
        // 1. 枚举从不同位置开始的最⻓重复⼦串
        // 枚举起始位置
        for (int i = 0; i < n; i++)
        {
            // 创建⼀个哈希表,统计频次
            int hash[128] = { 0 };
            // 寻找结束为⽌
            for (int j = i; j < n; j++)
            {
                hash[s[j]]++; // 统计字符出现的频次
                if (hash[s[j]] > 1) // 如果出现重复的
                    break;
                // 如果没有重复,就更新 ret
                ret = max(ret, j - i + 1);
            }
        }
        // 2. 返回结果
        return ret;
    }
};

注意:因为题目说明都为字符,所以我们可以创建一个128大小的数组用来模拟哈希表,没有必要真的申请一个哈希表出来。 

然后才能依据这一暴力枚举的底子我们做优化。

思路:

当right指向重复字符时,我们是否需要直接让right回退,left++呢?

其实如果right指向重复字符了,那么就证明此时就是left开头代表的窗口的最长字串了(因为在这之前right没有指向重复字符),所以此时不需要移动right,而应该移动left直到没有重复字符为止,然后再移动right即可。

  • 如果这个字符出现的频次超过1 ,说明窗口内有重复元素,那么就从左侧开始划出窗口,直到这个元素的频次变为1,然后再更新结果。
  • 如果没有超过1 ,说明当前窗口没有重复元素,可以直接更新结果。

所以『 更新结果』我们可以统一放在『 判断』的外面。

 有了思路,画图独立完成代码,不要直接看博主的代码。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128]={0};
        int left=0,right=0,len=0;
        int n=s.size();
        while(right<n)
        {
            hash[s[right]]++;//进入窗口
            while(hash[s[right]]>1)//判断
                hash[s[left++]]--;//离开窗口
            len=max(len,right-left+1);//更新结果
            right++;
        }
        return len;
    }
};

3.最大连续1的个数Ⅲ

1004. 最大连续1的个数 III - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/max-consecutive-ones-iii/description/

给定一个二进制数组 nums 和一个整数 k,如果可以翻转最多 k 个 0 ,则返回 数组中连续 1 的最大个数 。

 虽然题目中说是要翻转数字,但我们要的最终结果与翻转无关,何况如果真的实现翻转反而变得复杂,所以这里我们没有必要真的翻转。

仔细阅读题目,其实就是求数组中⼀段最长的连续区间,要求这段区间内 0 的个数不超过 k 个。

既然是连续区间,可以考虑使用『 滑动窗口』来解决问题。

思路:

设置一个 0 计数器 zero。

如果 right 遇到 0,我们应该让 zero ++ ,如果 right 遇到 1,直接跳过即可,这就是『 进入窗口』。

判断什么呢?当然是『 判断』zero是否超过 k,如果超过 就『 离开窗口』,每轮『 更新结果』。

 有了思路,画图独立完成代码,不要直接看博主的代码。

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int left=0,right=0,zero=0;
        int n=nums.size();
        int ret=0;
        while(right<n)
        {
            if(nums[right]==0)//进入窗口
                zero++;
            while(zero>k)//判断
            {
                if(nums[left++]==0)//离开窗口
                    zero--;
            }
            ret=max(ret,right-left+1);//更新结果
            right++;
        }
        return ret;
    }
};


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

1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/minimum-operations-to-reduce-x-to-zero/description/

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。

如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。

阅读题目后,我们发现解决方案可能比较复杂,因为既有可能从左面,也有可能从右面,还有可能一左一右等等,这么多复杂的情况,所以我们需要尝试对问题做转化,这也就是『 正难则反』的思路。 

如上图,我们可以求 target 这一区域最长时,那么此时就对应着最小操作数,最小操作数就等于数组元素个数减去target区域的元素个数。

所以我们成功转化出『 滑动窗口』问题。

  • 如果 sum < target ,右移右指针,直至变量和大于等于 target ,或右指针已经移到头;
  • 如果 sum > target ,右移左指针,直至变量和小于等于 target ,或左指针已经移到头;
  • 如果经过前两步的左右移动使得 sum == target ,维护满足条件数组的最大长度,并让下个元素进入窗口;

 有了思路,画图独立完成代码,不要直接看博主的代码。

class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int sum=0;
        for(int a : nums)
        {
            sum+=a;
        }
        int target = sum-x;
        if(target<0) return -1;//处理细节
        int left=0,right=0,tmp=0;
        int n=nums.size();
        int len=-1;
        while(right<n)
        {
            tmp+=nums[right];//进入窗口
            while(tmp > target)//判断
                tmp-=nums[left++];//离开窗口
            if(tmp==target)
                len=max(len,right-left+1);//更新结果
            right++;
        }
        if(len==-1) return len;
        else return n-len;
    }
};

=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微服务学习 | Springboot整合Dubbo+Nacos实现RPC调用

&#x1f3f7;️个人主页&#xff1a;鼠鼠我捏&#xff0c;要死了捏的主页 &#x1f3f7;️系列专栏&#xff1a;Golang全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…

vue打包优化,webpack的8大配置方案

vue-cli 生成的项目通常集成Webpack &#xff0c;在打包的时候&#xff0c;需要webpack来做一些事情。这里我们希望它可以压缩代码体积&#xff0c;提高运行效率。 文章目录 &#xff08;1&#xff09;代码压缩&#xff1a;&#xff08;2&#xff09;图片压缩&#xff1a;&…

Doris ——SQL原理解析

目录 前言 一、Doris简介 二、SQL解析简介 2.1 词法分析 2.2 语法分析 2.3 逻辑计划 2.4 物理计划 三、Doris SQL解析的总体架构 四、Parse阶段 五、Analyze阶段 六、SinglePlan阶段&#xff08;生成单机逻辑Plan阶段&#xff09; 七、DistributedPlan计划&#xf…

如何将阿里云服务器迁移

&#x1f4d1;前言 本文主要是如何将阿里云服务器迁移实现数据转移的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️** &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日…

MySQL篇之SQL优化

一、表的设计优化 表的设计优化&#xff08;参考阿里开发手册《嵩山版》&#xff09;&#xff1a; 1. 比如设置合适的数值&#xff08;tinyint int bigint&#xff09;&#xff0c;要根据实际情况选择。 2. 比如设置合适的字符串类型&#xff08;char和varchar&#xff09…

Redis背后的神奇力量:为何它如此高效?

Redis的速度快主要有以下几个原因&#xff1a; 1、基于内存操作 Redis的操作都是基于内存的&#xff0c;数据存储在内存中&#xff0c;而内存的读写速度远远快于硬盘&#xff0c;内存的运行速度比硬盘高出几个数量级&#xff0c;就像从翻阅书籍变成即刻在线信息查询&#xff0…

云计算实训室建设方案2024

云计算课程体系 云计算实训课程体系设计依据 一、培养目标 唯众公司提供创新技术教育和服务的目标&#xff0c;旨在提高人才培养质量&#xff0c;扩大就业创业&#xff0c;推动经济转型升级&#xff0c;以及培育新的经济发展动能。唯众公司提供云计算、大数据、人工智能等创…

网络原理(HTTP篇)

网络原理HTTP 前言HTTPHTTP的工作流程抓包工具抓取HTTP报文HTTP报文格式 请求报文具体细节首行URLURL的基本格式URL encode 方法 报头(header)HostContent-Length 和 Content-TypeUser-Agent&#xff08;UA&#xff09;RefererCookie&#xff08;重要&#xff09; 前言 如图&a…

汽车金融市场研究:预计2029年将达到482亿美元

汽车金融公司作为汽车流通产业链的重要一环&#xff0c;认真贯彻落实国家有关政策&#xff0c;采取多种措施助力汽车产业发展&#xff0c;为促进推动汽车消费、助力畅通汽车产业链、支持稳定宏观经济大盘发挥了积极作用。 益于国内疫情得到有效控制&#xff0c;我国经济持续稳定…

TypeScript(一):TypeScript基本理解

TypeScript基本理解 为什么使用TS JavaScript发展至今&#xff0c;没有进行数据类型的验证而我们知道&#xff0c;在编程阶段&#xff0c;错误发现的越早越好而TS就解决了JS的这个问题 认识TypeScript TypeScript是拥有类型的JavaScript超级&#xff0c;它可以编译成普通、…

OpenAI发布Sora模型,可根据文字生成逼真AI视频

早在2022年11月30日&#xff0c;OpenAI第一次发布人工智能聊天机器人ChatGPT&#xff0c;随后在全世界掀起了人工智能狂潮&#xff0c;颠覆了一个又一个行业。在过去的一年多的时间里&#xff0c;chatGPT的强大功能改变了越来越多人的工作和生活方式&#xff0c;成为了世界上用…

达梦数据库——数据迁移sqlserver-dm报错问题整理

报错情况一&#xff1a;Sql server迁移达梦连接报错’驱动程序无法通过使用安全套接字Q层(SSL)加密与SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client preferencesITLS127‘ 原因&#xff1a;历史版本的SOL SERVER服务…

防御保护第五次作业

1,办公区设备可以通过电信链路和移动链路上网(多对多的NAT,并且需要保留一个公网IP不能用来转换) FW5&#xff1a; 2,分公司设备可以通过总公司的移动链路和电信链路访问到DMz区的http服务器 FW5&#xff1a; 注&#xff1a;记得通过安全策略放行 分公司FW3 注意&#xff1a…

用300万支电动牙刷发起DDoS攻击?假的!

近日国外“300万支电动牙刷被用于DDoS攻击”的安全事件引发广泛讨论。国外媒体发文称“300万支电动牙刷被黑客用恶意软件感染&#xff0c;以执行分布式拒绝服务&#xff08;DDoS&#xff09;攻击。”经Fortinet与媒体确认&#xff0c;这是一起虚假的新闻。 上周&#xff0c;瑞士…

【网络编程】ZeroMQ的网络通信

文章目录 1、概述2、通信效果2.1、Request-Reply&#xff08;请求-响应模式&#xff09;2.2、Publish-Subscribe&#xff08;订阅-发布模式&#xff09; 3、方式选择3.1、准备用 Visual Studio-C 方式3.1.1、找到 Builds 文件夹3.1.2、查看 deprecated-msvc 下的 libzmq.sln 文…

图像像素读写image.at、image.ptr、指针

image.at 在OpenCV中&#xff0c;使用Mat对象表示图像数据&#xff0c;在使用at方法时&#xff0c;需要确保使用正确的数据类型&#xff08;如uchar或Vec3b&#xff09;&#xff0c;这取决于图像的通道数和数据深度。 单通道图像 对于单通道图像&#xff08;如灰度图像&…

正信晟锦:借钱后不还算诈骗吗

在探讨“借钱后不还”这一行为是否构成诈骗时&#xff0c;我们应首先明确诈骗的法律定义。根据《中华人民共和国刑法》&#xff0c;诈骗是指以非法占有为目的&#xff0c;采用虚构事实或隐瞒真相的手段&#xff0c;骗取他人财物的行为。关键在于是否存在欺诈行为和非法占有的主…

12.QT文件对话框 文件的弹窗选择-QFileDialog

目录 前言&#xff1a; 技能&#xff1a; 内容&#xff1a; 1. 界面 2.信号槽 3.其他函数 参考&#xff1a; 前言&#xff1a; 通过按钮实现文件弹窗选择以及关联的操作 效果图就和平时用电脑弹出的选文件对话框一样 技能&#xff1a; QString filename QFileDialog::ge…

消毒柜行业分析:市场渗透率不足20%

目前消毒柜仍然属于“小众”品类&#xff0c;疫情前期市场渗透率也不足20%。有业内人士表示&#xff0c;多年来消毒柜零售量规模基本在400万台左右徘徊&#xff0c;这个角度看&#xff0c;消毒柜是具有自身的产品消费人群的&#xff0c;其市场相对稳定&#xff0c;而且消毒柜的…

【Java EE初阶十六】网络原理(一)

在网络原理中主要学习TCP/IP四层模型中的重点网络协议 1. 应用层 1.1 应用程序与协议 应用层是和程序员接触最密切的&#xff1b; 应用程序&#xff1a;在应用层这里&#xff0c;很多时候都是程序员自定义应用层协议&#xff08;步骤&#xff1a;1、根据需求&#xff0c;明确…