【优选算法】Sliding-Chakra:滑动窗口的算法流(上)

文章目录

本篇是优选算法之滑动窗口算法,该算法常用于求一个移动的区间

1.概念解析

🚩什么是滑动窗口算法?

把一个较长的序列(比如数组、字符串等),划分成一个个固定长度或者动态长度的 "子序列",这个子序列就被称作窗口 。好比通过一个固定大小的窗框在一幅长画卷上逐步移动,每次窗框圈定的部分就是一个窗口内容,窗口会按照特定的规则在序列上 "滑动",常见的是每次移动一个元素的位置,新元素进入窗口,同时最靠前的旧元素移出窗口,借此不断更新窗口内的数据集合

2.长度最小的子数组

✏️题目描述:

✏️示例:

传送门: 长度最小的子数组

题解:

💻第一步:

以示例1为例子,如果使用暴力枚举,那么从 2 开始一直向后扩展区间找子集,然后再从开始以此往复,所有的子数组和都枚举一遍显然十分冗余,时间复杂度为O(n²)

说明我们要减少不必要的子数组来优化,如果使用双指针那样异侧指针的话,从两侧缩小来找子集会漏掉一些情况,所以可以考虑同侧指针结合单调性来解决问题

💻第二步:

还是 left 和 right ,都从索引为 0 开始,right 一直向右移动直到该区间的和大于 target 停止,然后 left 一直向右移动寻找是否还有能满足大于 target 的区间,直到小于等于 target 为止,以此往复就能找到所有有效的区间,更新结果的位置是不确定的,要根据题意来

具体流程大致如图,元素进入区间,循环判断,元素出区间

💻细节问题

代码用了两层循环似乎是O(n²),但实际上right和left分别往后遍历数组的时间复杂度为O(n+n) = O(2n) = O(n)

💻代码实现:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

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;
    }
};

3.无重复字符的最长数组

✏️题目描述:

✏️示例:

传送门: 无重复字符的最长数组

题解:

本题的大意为找到一段子区间每个字符都只出现一次,没有重复

💻第一步:

遇到这种求子区间优先思考用滑动窗口来解决,因为本题需要统计每个数出现的次数,判断其是否重复,索性可以利用哈希表解决重复类的问题

💻第二步:

通常滑动窗口的格式是很固定的,只有更新数据的地方需要灵活变动

先让第一个数据录入,即进窗口,判断不断循环,然后right依次向后移并不断往哈希表录入每个位置字符和更新结果,直到哈希表内某个字符的数据为2;此时left减去第一个数据,即出窗口,判断不断循环,然后不断向后移直到数据为2的字符数据变为1,再次开始更新数据

💻代码实现:

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class Solution 
{
public:
    int lengthOfLongestSubstring(string s) 
    {
        int hash[200] = { 0 };
        int ret = 0, n = s.size();
        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;
    }
};

4.最大连续1的个数

✏️题目描述:

✏️示例:

传送门: 最大连续1的个数

题解:

本题题意为在选取的某个子区间里能够反转0为1,只能反转k个,在此前提下找到最长的连续为1的子数组

💻第一步:

求子区间依然是以滑动窗口算法为主,不过我们要统计0出现的次数,但并不像无重复字符的最长数组那题一样需要使用哈希表来处理次数,毕竟这里只有两个数,显得有点太麻烦了,只用在right向右移动时遇到0时创建一个计数器++就行了

💻第二步:

主要的出入窗口流程如图所示

先让第一个数据录入,即进窗口,判断不断循环,然后right依次向后移并不断往计数器录入0和更新结果,直到计数器0的数据大于k;此时left减去第一个数据,即出窗口,判断不断循环,然后不断向后移直到0的出现次数小于等于k,再次开始更新数据

💻代码实现:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

class Solution 
{
public:
    int longestOnes(vector<int>& nums, int k) 
    {
        int ret, zero = 0;
        int n = nums.size();
        for (int left = 0, right = 0; right < n; ++right)
        {
            if (nums[right] == 0)
            {
                zero++;
            }
            while (zero > k)
            {
                if (nums[left++] == 0)
                {
                    zero--;
                }
            }
            ret = max(ret, right - left + 1);
        }
        return ret;
    }
};

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

✏️题目描述:

✏️示例:

传送门: 将x减到0的最小操作数

题解:

本题的题意相对来说有点难以理解,但结合示例之后就能明白。就是每次从最左边或者最右边选取数字,让x依次减去这几个数,求减到0最少需要几个数,若没有能减到零,就返回-1

💻第一步:

显然该题如果左边拿一点数,右边拿一点数,显然是很难考虑到所有的情况的,那么我们在写算法题的时候,通常正面遇到难以解决的问题时,可以考虑反面,即正难则反

a、b区间表示在获取数过程中所得到的数,那么sum表示整个数组的和,减去a+b=x就是剩下的一段连续区间,显然根据a、b选择的数不同,sum-x的区间长度位置也会不同,潜移默化中又回到了滑动窗口的问题上

💻第二步:

因此我们只需要找到一段连续区间sum1符合sum1=target=sum-x

先统计整个数组的和,让第一个数据录入,即进窗口,判断不断循环,然后right依次向后移并不断往sum1录入数据,符合要求则更新结果,直到sum1大于target=sum-x;此时left减去第一个数据,即出窗口,判断不断循环,然后不断向后移直到sum1小于target

💻代码实现:

cpp 复制代码
#include <iostream>
#include <vector>
using namespace std;

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, n = nums.size();
        for (int left = 0, right = 0, sum1 = 0; right < n; ++right)
        {
            sum1 += nums[right];
            while (tmp > target)
            {
                sum1 -= nums[left++];
            }
            if (sum1 == target)
            {
                ret = max(ret, right - left + 1);
            }
        }
        return ret == -1 ? ret : n - ret;
    }
};

希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

相关推荐
CoderYanger1 分钟前
Java EE 进阶:5. Spring IoC&DI(第二弹):Spring IoC
java·开发语言·spring boot·spring·面试·职场和发展·java-ee
骑着蜗牛撵大象32716 分钟前
558 万美元训出 671B 模型:DeepSeek-V3 算法与系统的协同拆解
分布式·算法·moe·deepseek-v3·mla·fp8训练
Eyfcom18 分钟前
〖阶段性回顾〗物流系统架构与算法的 Day 1-5 知识图谱与实战答疑
算法·系统架构·知识图谱·物流管理系统·快递管理系统
xunberg23 分钟前
【Agent 生产化手记-02】Agent 怎么向人提问
人工智能·算法·架构
海绵宝宝转agent29 分钟前
Leetcode二叉树算法个人理解
笔记·学习·算法·leetcode·深度优先
青峰讲运维39 分钟前
OpenStack Heat 编排:用模板一键交付云业务,告别手动点页面
开发语言·php·openstack
悟天特斯1 小时前
安防一体化:从“各管各的“到“全局联动“
java·大数据·网络·人工智能·物联网
pjj198541 小时前
YOLO 目标检测算法系列梳理:从 V1 到 V5 的原理演进
算法·yolo·目标检测
70后写java的老码农1 小时前
Spring Boot 3.x + ShardingSphere 5.x + MyBatis-Plus 3.x:订单分库分表实战(含两个踩坑记录)
java
MyFreeIT1 小时前
Python Windows Manual
开发语言·python