用滑动窗口代替暴力枚举:算法新手的第二道砍

文章目录

前言

介绍

长度最小的子数组

1.题目解析

2.算法原理

3.编写代码

无重复字符的最长子串

1.题目解析

2.算法原理

3.编写代码

[将 x 减到 0 的最小操作数](#将 x 减到 0 的最小操作数)

1.题目解析

2.算法原理

3.编写代码

找出字符串中所有字母异位词

1.题目解析

2.算法原理

3.编写代码

总结


前言

接上一篇的算法内容,这篇来讲解"滑动窗口"。


介绍

滑动窗口是算法中基于同向双指针的高效方法,通过左右两个指针在数组、字符串等结构上同向移动,形成一个 "窗口"。它通过动态调整窗口的左右边界,在 O (n) 时间复杂度内解决子数组、子串的统计类问题,如最长无重复子串、最大子数组和等;它也避免了暴力枚举的高复杂度。其核心是利用同向指针的协同移动,高效维护窗口的有效范围以完成统计或优化目标。


长度最小的子数组

1.题目解析

这道题是中等难度的题目,看完题目结合例子来分析这道题的要求。

画图来分析:题目要求需要一个数组里的元素之和大于等于7且长度是最小的;从上图可看出,有两组数组的元素之和大于或者等于7,但两组长度不一样,显而易见第二组数组满足条件,则该例题从原则上是数组[ 4, 3 ] 满足条件。

2.算法原理

通常最优的解法基本上是通过暴力解法进行优化得出,此题我将直接采用最优解法------滑动窗口。

由自己画图来分析,我们可以发现一直往后面遍历,满足条件的小数组就越多而长度也在增加;因此我们可以设定在得到第一个满足条件的小数组就更新结果,并让left++

只要出现满足条件的数组,就让left++;相当于right用来扩展,left用来收缩

3.编写代码

cpp 复制代码
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n =nums.size(),sum=0,len=INT_MAX;//INT_MAX 是一个预定义的常量
        for(int left=0 ,right=0;right<n;right++)
        {
            sum+=nums[right];//更新结果
            while(sum>=target)//进窗口
            {
                len=min(len,right-left+1);//计算当前有效窗口的长度(right - left + 1),与 len 比较取最小值,更新 len
                sum-=nums[left++];//left 右移一位(收缩窗口)
            }
        }
        return len == INT_MAX?0:len;//如果没有找到>=target的数据就返回0;找到就返回len
    }
};

扩展知识点:

INT_MAX是一个预定义的常量。

INT_MAX 的核心功能是标记 int类型的最大值边界,常见于:

  1. 初始化需找「最小值」的变量;
  2. 表示「未找到目标」的无效状态;
  3. 数值运算中的溢出判断。

无重复字符的最长子串

1.题目解析

老样子,做题前,咱们一定要先看题分析再动手;并且亲自手动画图来分析例子,题目要求返回最长子串的长度

2.算法原理

看了上题,我们可以大概明白很多算法题都可以进行暴力解法,只是暴力解法不算最优解法;只要通过分析我们发现可以利用"同向双指针"的原理,来判断一道算法题是否能使用"滑动窗口"来解答。重点就在于:left=0,right=0,这样两指针才能同方向前进。

本题要结合hash表来判断字符重复出现 (如果对hash表不明白,可以去查阅一下资料或者相关的博客,(^^))

先让right开始走,left不变,往前走就把字符带进hash表中记录进去

没有出现重复的字符就一直往前走并记录结果;当出现重复的字符时,第二次出现的字符已经进入hash表中,就要先hash--,把重复的字符踢出去再让原先大子串中left++

让记录进去的结果进行比较,最大的子串就出来了;最后right++,并返回结果

3.编写代码

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int hash[128]={0}; //创建一个数组用来模拟hash表,这样不用创建一个hash表
        //第一层用for/while循环都可以
        //练手,我用while
        int left=0,right=0,n=s.size();
        int ret=0;
        while(right<n)
        {
            //进窗口就是进入hash表,用hash表来判断
            hash[s[right]]++;
            //判断是否出现重复字符和是否出窗口
            while(hash[s[right]] > 1) //这样大于1的意思是:如当数组中a出现2次,就判断检查的数组中有重复字符出现
            {
                hash[s[left++]]--; //先--再++ ; 先让左边第一个出来,再让left++
            }
            ret=max(ret,right-left+1);
            //闭区间长度的计算规则:终点索引 - 起点索引 + 1(比如 left=0、right=2 时,窗口是 [0,1,2],长度是 2-0+1=3,而非 2);
            //C++ 中的库函数,作用是比较 a 和 b,返回更大的那个值
            right++;
        }
        return ret;
    }
};

将 x 减到 0 的最小操作数

1.题目解析

看问题,明白题目的要求,开始画图分析

2.算法原理

结合上面俩个题目的解法,我们可以归纳成4个步骤:

1.left=0,right=0

2.进窗口

3.判断什么时候出窗口

4.更新结果

3.编写代码

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        //先求数组元素之和
        int sum=0,ret=-1; //取-1是因为不满足条件就返回-1
        //用范围for
        for(int a:nums) 
            sum+=a;
        int target=sum-x; //需要的最长数组元素之和
        if(target<0) //细节处理
            return -1;
        for(int left=0,right=0,tmp=0;right<nums.size();right++) //用tmp记录[left,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; //数组长度-子数组的长度
    }
};

找出字符串中所有字母异位词

1.题目解析

看题目并结合例子来理解题目要求

2.算法原理

先用两个hash表来判断两个字符串是否是"异位词",

hash1用来记录字符串p中的字符,hash2用来记录字符串s中的字符,

比较两个hash表中的字符是否出现相同的字母

结合滑动窗口+hash表的方法来解决问题

1.先确定同向的双指针:left = 0 , right = 0

2.进窗口的方式:hash2[ int ]++,从左边开始进入,就开始计数 count ++

3.判断出窗口的条件:hash2表记录的字符串长度只能等于hash1表字符串的长度

4.出窗口的方式:hash2[ out ]-- , 从左边开始删除字符,在出窗口前就 count--

5.更新结果

3.编写代码

cpp 复制代码
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        //滑动窗口+hash表
        //题目要求结果以数组类型返回
        vector<int> ret;
        //用hash1把p中的字符串记录下
        int hash1[128] = {0};
        for(auto ch :p) 
            hash1[ch-'a']++; //题目要求是小写字母
        //用hash2来查找符合的子串
        int hash2[128]={0};
        int m=p.size();
        for(int left=0,right=0,count=0;right<s.size();right++)
        {
            //进窗口
            char in=s[right];
            if(++hash2[in-'a'] <= hash1[in-'a'])
                count++; //记录字符
            
            //出窗口
            if(right-left+1 > m)
            { 
                char out=s[left++];
                if(hash2[out-'a']-- <= hash1[out-'a'])
                     count--; //字符类型不能自增和自减,不要写成out--
            }
               
            //更新结果:是跟进出窗口一起的
            if(count==m)
                ret.push_back(left); //要返回下标数
        }
        return ret;

    }
};

总结

非常感谢大家阅读完这篇博客。希望这篇文章能够为您带来一些有价值的信息和启示。如果您发现有问题或者有建议,欢迎在评论区留言,我们一起交流学习。

相关推荐
ulias2122 小时前
list的实现和使用
数据结构·windows·list
涛声依旧Cjt2 小时前
Spring AOP实战--优雅的对出参进行脱敏
java·spring·springaop 实战·aop 优雅脱敏·spring 脱敏
free-elcmacom2 小时前
机器学习进阶<10>分类器集成:集成学习算法
python·算法·机器学习·集成学习
灰乌鸦乌卡2 小时前
练手项目0 介绍
java
以孝治家行动2 小时前
学孝道故事 传家国情怀——慈明学校以孝治家阳光家教中心开展线上学习
学习·以孝治家·正能量
laocooon5238578862 小时前
C语言枚举知识详解与示例
java·c语言·数据库
全栈陈序员2 小时前
【Python】基础语法入门(十八)——函数式编程初探:用 `map`、`filter`、`reduce` 和 `lambda` 写出更简洁的代码
开发语言·人工智能·python·学习
月明长歌2 小时前
【码道初阶】【LeetCode 160】相交链表:让跑者“起跑线对齐”的智慧
java·算法·leetcode·链表
菜鸟小芯2 小时前
OpenHarmony环境搭建——02-JDK17安装教程
java