【基础算法总结】滑动窗口一

news2024/11/20 15:24:08

滑动窗口

  • 1.长度最小的字数组
  • 2.无重复字符的最长子串
  • 3.最大连续1的个数 III
  • 4.将 x 减到 0 的最小操作数

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.长度最小的字数组

题目链接:209.长度最小的字数组

题目分析:

在这里插入图片描述
注意题目说的是正整数数组,说明数组里面的数是大于等于0的数。因此这道题我们有一种优化的方法。

题目让找连续的子数组和大于或等于target,并且长度最小。有很多种情况,但是我们选择的是最小长度。
在这里插入图片描述

算法原理

不管什么题,首先我们一定会先想到的是暴力求解,因为只有暴力求解出来了,我们就可以在暴力求解的基础上进行优化!

解法一:暴力枚举出所有的子数组的和
两层for循环,O(N^2)
在这里插入图片描述

注意到此时暴力枚举是有优化的。之前是每次都从前往后走一遍,但是现在定义一个left,一个right初始化都指向0下标,sum+=nums[right],++right。
等到符合要求统计一下长度,

注意题目说的是一个正整数数组,都是大于等于0的数,这个sum是呈现出递增的状态的,单调递增!

在暴力求解中,此时right还要++,但是注意题目本来要求的就是最小长度,此时sum在加上往上走了一步的right的num[right],一定是满足sum>=target,但是len变成5了,一定不会是最终结果,因此当条件已经满足sum>=target ,right就不用动了。right后面也就不用再枚举了。

在这里插入图片描述
那现在让left+1,right和left指向同一下标,然后再重复上面过程,那有个问题,这段区间的和能不能直接算出来?

当然可以。现在sum=8,我只需要把让sum减去num[left],不就是现在left和right所在的区间和算出来吗。没有必要让right傻傻的回退然后重新加。因此right不动,更新sum=6.

在这里插入图片描述
因此我们从暴力枚举中发现两个优化:
一个是right后面不用枚举,一个left++,right不用回退,

所以我们可以使用双指针优化。

解法二:利用单调性,使用 “同向双指针” 来优化

当我们在暴力枚举的策略中发现left和right都是从左向右一个方向移动,我们就称为这两个指针叫做同向指针。同向双指针又称为滑动窗口。

什么是滑动窗口?
本质上是 “同向双指针”,left从左到右移动,right不回退,从左到右移动,用left和right一直维护这个区间的和,然后这两个指针从左向右移动的过程非常像一个窗口在这个数组里滑来滑去。

在这里插入图片描述

什么时候用滑动窗口?
利用单调性,用滑动窗口解决问题。
当我们发现在暴力求解时,两个指针都可以做到不回退,都是向同一个方向移动的时候,此时就可以用滑动窗口。

滑动窗口怎么用?

  1. 初始left=0,right=0,充当窗口左端点,右端点。用left,right标记窗口左区间,右区间。
  2. 进窗口(++right)
  3. 判断
    根据判断决定是否出窗口(++left)
  4. 更新结果
    2,3都有可能会更新结果,看题目要求

进窗口,判断,出窗口一直循环,直到right超过区间长度结束,更新结果看题目要求(进窗口,出窗口都有可能),

在这里插入图片描述

滑动窗口正确性
暴力枚举肯定对的,因为已经把所有子数组的情况都找出来了。虽然滑动窗口并没有把没有把所有情况都枚举出来,但是这里利用单调性,规避了很多没有必要的枚举行为。虽然没有把所有情况真正枚举出来,但是已经判断出有些子数组不是最终结果,已经把所有结果都考虑进来了,所以这种策略是跟暴力枚举是一样正确的。

滑动窗口时间复杂度
进窗口是一个循环,判断也是一个循环。两层循环套在一起。你会觉得时间复杂度O(N^2),但是不能看代码算时间复杂度,要看实际情况分析实际复杂度。实际我们只会让right向前移动,left也向前移动,即使时最坏情况,right移动到最后一个元素,lefi也移动到最后一个元素,因为总共操作次数最多n+n次 整体时间复杂度O(N)
在这里插入图片描述

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;


        // // 1.初始窗口
        // int left=0,right=0,len=INT_MAX;
        // int n=nums.size(),sum=0;
        // while(right<n)//2.进窗口
        // {
        //     sum+=nums[right];
        //     while(sum>=target)
        //     {
        //         //4.更新结果
        //         if(right-left < len)
        //             len=right-left;

        //         //3.出窗口
        //         sum-=nums[left];
        //         ++left;
        //     }
        //     ++right;
        // }
        // return len==INT_MAX?0:len+1;

    }
};

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

题目链接:3. 无重复字符的最长子串

题目分析:
在这里插入图片描述
子串和子数组都是连续的

在这里插入图片描述
算法原理

首先还是暴力枚举,然后根据暴力枚举进行优化。
以下面为例,两层for循环,但是下面找到的结果都是我们站在上帝角度,编译器并不知到什么时候结束。一般对应判断是否有重复元素,我们都可以用哈希表来解决问题。

使用哈希表,判断是否有重复元素,比如让你判断一个数组是否有重复,或者两个数组是否有重复都可以用哈希映射!
在这里插入图片描述
解法一:暴力枚举+哈希表(判断字符是否重复出现)
O(N^2)

根据解法一做优化,定义一个left,right指针。当right走到有重复的元素后,已经找到一个字串,其中left到right区间每个元素都已经进入hash表。
在这里插入图片描述

此时left向前走一步,但是这个区间还是有重复元素,因此left要走到没有重复的区间才行,
在这里插入图片描述

然后这个时候以前做法是right回退然后重新往下走,但是这里left到right区间元素本来就在hash表里,因此就不需要right回退了,而是向right继续向前走。然后重复上面过程,直到right走到结尾。结束!
在这里插入图片描述

这不就是滑动窗口的思想吗。双向指针,left往前走,right不回退一直往前走!

解法二:利用规律,使用 “滑动窗口” 解决问题

  1. left=0,right=0
  2. 进窗口
  3. 判断
    出窗口
  4. 更新结果

进窗口、判断、出窗口,更新结果是一个大循环过程。直到right到结尾循环结束。其中判断、出窗口是一个小循环。不过时间复杂度还是O(N).

注意更新结果可能在进窗口后,判断后,出窗口后,判断后任意一个地方,看题目要求

本题:

进窗口 ->-> 让字符进入哈希表
判断-> 窗口内出现重复元素
出窗口-> 从哈希表中删除该字符

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128]={0};//使用数组模拟哈希表
        int n=s.size(),ret=0;
        for(int left=0,right=0;right<n;++right)
        {
            hash[s[right]]++; // 进窗口
            while(hash[s[right]] > 1) // 判断
            {  
                hash[s[left++]]--;//出窗口      
            }
            ret=max(ret,right-left+1);//更新结果
        }
        return ret;
    }
};

总结一下:
利用单调性,使用 双指针 解决问题。
一般left和right,一个指向数组最左边,一个指向数组最右边,然后一次可以排除一批,再然后left++,–right,两个指针是对撞的

这里利用单调性或者利用规律,使用 滑动窗口 解决问题
滑动窗口也使用双指针解决问题,不过left一直从前往后走,right不回退从前往后走,两个指针是同向的。因此滑动窗口本质其实是同向双指针

3.最大连续1的个数 III

题目链接:1004. 最大连续1的个数 III

题目分析:

在这里插入图片描述

题目说的翻转实际上是把0变成1的意思,最多翻转K次,说明小于等于K都是可以的。
在这里插入图片描述

拿到题我们开始肯定想的是暴力求解。如果直接暴力求解,遇到0->1了,那下一次在遍历就有问题了。因此我们换一个思路。这道题不是让转化后最大连续1的个数吗。我们转化为:找出最长的子数组,数组里0的个数不超过K个,这个数组里面0一定能够转化成1。

算法原理:

解法一:暴力枚举+zero计数器

伪代码,两层for循环,统计zero的个数,满足zero>k,统计此时数组长度,然后重新进入循环,注意每次zero都清0
在这里插入图片描述
然后我们根据暴力枚举,看看有没有优化的可能。定义两个指针left,right,right走到zero>k的位置,zero=3,大于k。
在这里插入图片描述
按照暴力求解left++,然后right回溯然后重新往后走。但是我们发现没有必要,现在left往前走一步,你会发现,right还是停留在老位置!这个区间不用在管的!直接丢弃。
在这里插入图片描述
因此,让left一直走到zero<=k的位置。然后right也根本不用回溯然后在重新走,而是直接往后走就行了。
在这里插入图片描述
根据上面的发现,当在暴力枚举中,发现left,right是同向移动的,利用这个规律,使用滑动窗口解决问题

解法二:利用规律,使用滑动窗口

  1. left=0,right=0
  2. 进窗口
  3. 判断
    出窗口
  4. 更新结果

进窗口 -> 如果是1,不理会。如果是0,计数器+1
判断 -> zero>k
出窗口 -> 如果是1,不理会。如果是0,计数器-1
更新结果:在判断之后在更新

在这里插入图片描述

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {

        int n=nums.size(),len=0,zero=0;
        for(int left=0,right=0;right<n;++right)
        {
            // 进窗口
            if(nums[right] == 0)
                ++zero;
            
            while(zero>k)//判断
            {
                if(nums[left++] == 0) //出窗口
                    --zero;
            }

            //更新结果
            len=max(len,right-left+1);
        }
        return len;

    }
};

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

题目链接:1658. 将 x 减到 0 的最小操作数

题目分析:

在这里插入图片描述

这道题让每次从数组左右两边移除一个数,然后就是一个新的数组,然后再从新的数组再从左右两边移除一个数。但是如果真的硬着头皮开始做,其实是很困难的。
你并不知道每次是从最左边走还是最右边找。有可能这次左边下次右边或者还是左边,情况太复杂了。

在这里插入图片描述

因此我们可以利用正难则反的思想
正对面解题太难,那就想对立面,换个思路。
不是每次从左右两端找一个数吗,那可能找到情况就是a+b=x,a、b什么情况都要,但是中间这个连续区间的和不也是确定的吗sum-x,也就是这道题我们转换成,找出最长的子数组长度,所有元素的和正好等于sum-x,然后数组总长减去这段子区间长度不就是问题答案吗,如果没找到说明这个数组不存在将x减到0的数,直接返回-1
在这里插入图片描述

解法一:暴力求解

初始left,right指向同一下标,当right走到和大于target的时候,left往前走,按照暴力求解,right要回到和left相同下标,然后right在重新往前走,直到再次走到和大于target的地方停下来,然后重复上面过程。
在这里插入图片描述

但是今天这里不需要right回溯,因为right回溯后重新走到下面的位置,因为left已经往前走了,这段区间的和肯定是更小了,因此就不需要right回溯了。要么right不动,要么right往后走。
同向双指针 ----> 本质就是滑动窗口
在这里插入图片描述
解法二:使用滑动窗口

在这里插入图片描述

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;

        sum = 0;
        int len = -1;
        for (int left = 0, right = 0; right < nums.size(); ++right)
        {
            sum += nums[right];//进窗口

            while (sum > target)//判断
            {
                sum -= nums[left++];//出窗口
            }

            if (sum == target)//更新结果
            {
                len = max(len, right - left + 1);
            }
        }

       if(len == -1) return len;
       else return nums.size()-len;
    }
};

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

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

相关文章

软件测试(实验五)——Jmeter的使用

目录 实验目的 一、使用JMeter演示取样器、监听器、配置元件、断言的使用&#xff1b; 1、取样器 2、监听器 3、配置元件的使用 ① 用户定义的变量 ②HTTP信息头管理器 ③HTTP请求默认值 ④CSV数据文件设置 4、断言 ①响应断言 ②JSON断言 ③断言持续时间 二、使用…

普通二维码打开微信小程序并且传递参数

实现方法&#xff1a; 【1】确保有一个企业级别的认证过的微信小程序 【2】有一个https并且备案过的域名 【3】进入微信后台“开发”-“开发设置”-“扫普通链接二维码打开小程序”-“添加” 官方文档&#xff1a;https://developers.weixin.qq.com/miniprogram/introduction/q…

C语言实验-学生信息管理系统

按以下菜单界面编写学生信息管理系统&#xff1b; 1&#xff09;录入学生信息首先输入学生人数&#xff0c;然后根据学生人数开辟动态数组&#xff1b; 2&#xff09;学生信息包括学号、姓名、性别、三门课成绩、总分&#xff1b;其中学号、姓名、 性别、三门课成绩是需要从键盘…

YOLO自研模块:多尺度轻量化卷积模块

目录 一、原理 二、代码 三、配置文件 一、原理 不同大小的卷积核,提取目标特征的特征尺度不同,所以通过使用不同大小卷积核的卷积来提取特征就可以保证获取到目标的多尺度特征。 借鉴YOLOv8中,将通道数进行划分的操作,在卷积的输入过程中为了减小参数量,将输入通道数…

截图时,VSCode屏幕泛白

问题如图所示&#xff1a; 放弃前摇&#xff0c;直接给出解决方案&#xff1a;换个主题即可。 实测&#xff0c;Light Modern 的色域正常&#xff0c;其他的没有经过测试。 出现这个问题的原因&#xff0c;大概率就是色彩空间不匹配。 HDR 内容是为了在支持 HDR 的显示设备上展…

H3C MSTP 实验

H3C MSTP 实验 实验拓扑 ​​ 实验需求 所有交换机上创建 Vlan10&#xff0c;Vlan20&#xff0c;Vlan30 和 Vlan40所有交换机之间的端口配置为 Trunk&#xff0c;并放行相关 VLAN按照图示分区域配置 MSTP&#xff0c;并配置主备根网桥 实验步骤 VLAN基础配置&#xff08;…

C++ 多态(二)

四、多态纯虚函数 纯虚函数是在C中用来定义抽象类的一种特殊函数。纯虚函数没有具体的实现&#xff0c;只有函数声明&#xff0c;它的作用是为派生类提供一个接口&#xff0c;让派生类必须实现这个函数。如果一个类中包含了纯虚函数&#xff0c;那么这个类就是抽象类&#xff…

史上最复杂的探测器嫦娥六号,如何采取人类首份月背样品? | 最新快讯

作者&#xff1a;LM-51D-YZ4D2&#xff0c;航天爱好者 今天&#xff0c;长征五号遥八火箭即将从海南文昌航天发射场点火起飞&#xff0c;把嫦娥六号探测器送入预定轨道。作为嫦娥五号的备份器&#xff0c;嫦娥六号继承了嫦娥五号的结构&#xff0c;又针对月球背面着陆进行了优化…

6.【Orangepi Zero2】localtime、asctime函数

【Orangepi Zero2】localtime、asctime函数 localtime、asctime localtime、asctime #include <time.h>struct tm *localtime(const time_t *timep); char *asctime(const struct tm *tm);localtime() 是 把从1970-1-1零点零分到当前时间系统所偏移的秒数时间转换为本地…

批量视频剪辑新选择:一键式按照指定秒数分割视频并轻松提取视频中的音频,让视频处理更高效!

是否经常为大量的视频剪辑工作感到头疼&#xff1f;还在一个个手动分割、提取音频吗&#xff1f;现在&#xff0c;我们为你带来了一款全新的视频批量剪辑神器&#xff0c;让你轻松应对各种视频处理需求&#xff01; 首先&#xff0c;进入媒体梦工厂的主页面&#xff0c;并在板…

后台架构总结

前言 疫情三年&#xff0c;全国各地的健康码成为了每个人的重要生活组成部分。虽然过去一年&#xff0c;但是回想起来任然历历在目。 今天我就通过当时基于小程序的健康码架构&#xff0c;来给大家讲一下如何基于java&#xff0c;springboot等技术来快速搭建一个后台业务系统…

freeRTOS任务通知(1-17)

任务通知简介&#xff1a; def&#xff1a; 任务通知是用来通知任务的&#xff0c;任务控制块中的结构体成员变量ulNotifiedValue就是这个通知值。 任务通知的内存消耗比较小 1&#xff1a; 使用队列&#xff0c;信号量&#xff0c;时间标志组都需要另外创建结构体&#xff…

C++设计模式-创建型设计模式

设计模式 设计模式是什么 设计模式是指在软件开发中&#xff0c;经过验证的&#xff0c;用于解决在特定环境下&#xff0c;重复出现的&#xff0c;特定问题的解决方案&#xff1b;其实就是解决问题的固定套路。但是要慎用设计模式&#xff0c;有一定的工程代码量之后用它比较…

ue引擎游戏开发笔记(29)——实现第三人称角色随手柄力度进行移动

1.需求分析 角色可以随手柄力量大小进行走路和跑步&#xff0c;不动时保持角色停顿。 2.操作实现 1.思路&#xff1a;通过动画蓝图和动画混合实现角色移动和输入的联系。 2.建立动画蓝图和混合空间&#xff1a; 3.在混合空间中对角色移动进行编辑&#xff1a; 4.在蓝图中设定变…

HTML_CSS学习:浮动

一、浮动简介 相关代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>浮动_简介</title><style>div{width: 600px;height: 400px;background-color: #1c80d9;}img{float:…

Java进阶【十三期】:【异常处理】 (抛出捕获异常、自定义异常处理)、处理异常的几种方式 【(File】文件路径操作、File文件处理的综合练习

文章目录 Java进阶【十三期】&#xff1a;异常处理一、异常基本介绍二、编译异常和运行异常三、总结 异常的作用异常的处理方式一、JVM默认的处理方式二、自己处理异常自己 处理的问题 三、总结 Throwable 成员方法抛出异常总结 异常练习自定义异常 FileFile 三个 构造方法File…

【SSM进阶学习系列丨分页篇】PageHelper 分页插件导入集成实践

文章目录 一、说明什么是分页PageHelper介绍 二、导入依赖三、集成Spring框架中四、编写Service五、编写Controller六、编写queryAllByPage页面展示数据 一、说明 什么是分页 ​ 针对分页&#xff0c;使用的是PageHelper分页插件&#xff0c;版本使用的是5.1.8 。 ​ 参考文档…

力扣hot100:543. 二叉树的直径/108. 将有序数组转换为二叉搜索树

一、543. 二叉树的直径 LeetCode&#xff1a;543. 二叉树的直径 二叉树的直径 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。 遇到二叉树的问题很容易去直接用求解的目标去定义递归函数。但是仔细考虑&#xff0c;返回树的直径并不能向上传播。因此我们可以拆…

三维球体空间中光线反射模拟与三维点云提取matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 三维球体空间中光线反射模拟与三维点云提取matlab仿真。设置一个三维的椭球模型&#xff0c;作为墙壁&#xff0c;然后根据光线的反射原理&#xff0c;设计三维空…

Linux内核--设备驱动(四)基础通信接口整理

目录 一、引言 二、I2C ------>2.1、虚拟总线 ------>2.2、I2C适配器序列号指定 ------>2.3、I2C驱动的注册 ------>2.4、I2C设备的创建及注册 ------>2.5、probe 三、I2S 四、DMA ------>4.1、MMU IOMMU 一、引言 本篇文章对于常见通讯接口的内…