【算法】滑动窗口解决力扣『将x减到0的最操作数』问题

🎬 个人主页MSTcheng · CSDN
🌱 代码仓库MSTcheng · Gitee
🔥 精选专栏 : 《C语言
数据结构
《算法学习》
C++由浅入深

💬座右铭: 路虽远行则将至,事虽难做则必成!

文章目录

一、滑动窗口的介绍

滑动窗口(Sliding Window) 是一种处理数组或字符串类算法题的常用优化技巧。它将嵌套的循环O(N^2)优化成单层循环O(N),从而显著的减低时间复杂度。

可以把它想象成一段在数组上滑动的"透明玻璃框"。

1、核心思想

滑动窗口主要用于解决连"续子序列"的问题

  • 传统暴力法: 枚举每一个起点,再从起点开始向后枚举,每一次都要重新去画一个框。
  • 滑动窗口法: 只需要维护两个指针leftright(leftright分别为左边界和右边界),通过right向右移动来吸收新元素,left++来排除旧元素。

2.、窗口的分类
(1) 固定窗口

窗口的大小 K 是固定的。

复制代码
场景:求长度为K的子数组的最大平均值。
操作:right每移动一步,left 也同步移动一步,保持 right - left + 1 == K。

(2) 可变窗口(最常见)

窗口的大小根据条件动态收缩。

复制代码
场景:求和为S的最短子数组;求不重复字符的最长子串。
操作:right 不断向右扩大窗口,当窗口内条件不再满足(或超过)要求时,left 开始向右移动收缩窗口。

2、滑动窗口与传统暴力的区别

二、利用滑动窗口解决将x减到0的最小操作数问题

1、题目展示

2、题目示例

3、题目解析

4、算法原理

思考一下:如果我们直接的按照题目的要求去选择数的话我们会发现有非常多种选法,比如我们可以按顺序从左往右依次去选择;或者直接从中间开始;也可以一左一右来选择。像这样正面操作比较困难我们就取它的反面来实现。

题目的要求是:我们在ab这个区间里选择一些数,然后用x减去这些数等于0的最小选择个数(这里从左右两倍选择来举例选择方法有很多种这是其中一种)。那我们的就取题目的反面,去找一个最长的区间,区间的数加起来等于sum-x,这里的sum是整个数组的总和。

为什么能这样?

思考一下,我现在的问题已经从我要去选择一些数,使得x减去这些数等于0,并且选择的数的个数越少越好的问题,转化成了找一个最长区间,使得这个区间的数加起来正好等于sum-x的问题,因为我们找到了一个区间等于sum-x,那么上图a区间和b区间的数加起来一定是等于x的,因为sum-x又加上x才等于sum!!!

具体的操作步骤:

5、代码示例

cpp 复制代码
class Solution {
public:
    int minOperations(vector<int>& nums, int x) {
        int size=nums.size();//计算nums的大小
        //定义两个指针
        int left=0,right=0;
        int total=0;//nums所有元素的总和
        //先遍历nums记录所有元素的总和
        for(int i=0;i<size;i++)
        {   
            total+=nums[i];
        }
        //特殊情况的处理
        if(total<x)
        {
            //意味删除所有元素都也无法凑成x
            return -1;
        }
        
        //滑动窗口
        int target=total-x; //
        int sum=0;
        int maxlen=-1;
        while(right<size)
        {
						//进入窗口
            sum+=nums[right];
            right++;
            //出窗口的条件 sum>target
            while(sum>target)
            {
                sum-=nums[left];
                left++;
            }
					 //跳出循环此时sum可能小于target也可能等于target
            //只有sum==target时更新结果
            if(sum==target)
            {
                int currlen=right-left;
                maxlen=max(currlen,maxlen);
            }
        }
        //跳出循环 要注意窗口内部的元素都不符合要求 所以maxlen不会更新
        if(maxlen==-1)
        {
            return -1;
        }
        //当right遍历到最后循环结束此时已经记录下了中间最大的长度 使用size-maxlen得到最小区间
        return size-maxlen;
    }
};

注意:totalnums所有元素的总和而sum是窗口leftright所包含的区间的总和!,以及最后maxlen的判断,maxlen默认为-1,如果进入循环都找不到一个sum等于target那么也就意味着maxlen没有更新,说明找不到一个区间ab使得x减去这个区间的数等于0,此时直接返回-1即可!

三、总结

本题我们使用了滑动窗口来解决,滑动窗口的特征是:

  1. 两个指针都向同一个方向去运动,且指针不会回退。
  2. 滑动窗口的步骤就是:进窗口,判断,出窗口,更新结果这几个步骤。

什么时候该用滑动窗口?

当你看到题目包含以下关键词时,可以考虑:

1、连续(子数组、子串)。

2、求最值(最长、最短、最大和)。

3、约束条件(和等于 K、不包含重复字符)。

相关推荐
会员源码网13 小时前
使用`mysql_*`废弃函数(PHP7+完全移除,导致代码无法运行)
后端·算法
木心月转码ing14 小时前
Hot100-Day10-T438T438找到字符串中所有字母异位词
算法
HelloReader15 小时前
Wi-Fi CSI 感知技术用无线信号“看见“室内的人
算法
颜酱17 小时前
二叉树分解问题思路解题模式
javascript·后端·算法
qianpeng89719 小时前
水声匹配场定位原理及实验
算法
董董灿是个攻城狮1 天前
AI视觉连载8:传统 CV 之边缘检测
算法
AI软著研究员2 天前
程序员必看:软著不是“面子工程”,是代码的“法律保险”
算法
FunnySaltyFish2 天前
什么?Compose 把 GapBuffer 换成了 LinkBuffer?
算法·kotlin·android jetpack
颜酱2 天前
理解二叉树最近公共祖先(LCA):从基础到变种解析
javascript·后端·算法
地平线开发者2 天前
SparseDrive 模型导出与性能优化实战
算法·自动驾驶