【Leetcode】“滑” 出新天地:滑动窗口法的思路转换与问题破解

前言

🌟🌟本期讲解关于力扣的几篇题解的详细介绍~~~

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

🔥 你的点赞就是小编不断更新的最大动力

🎆那么废话不多说直接开整吧~~

目录

📚️1.将x减小到零的最小操作数

🚀1.1题目描述

🚀1.2题目解析

🚀1.3题目代码

🚀1.4代码复杂度

📚️2.水果成篮

🚀2.1题目描述

🚀2.2题目解析

1.暴力枚举

2.滑动窗口

🚀2.3题目代码

1.哈希表

2.数组

🚀2.4代码复杂度

📚️3.总结

📚️1.将x减小到零的最小操作数

🚀1.1题目描述

这道题来自力扣上的题目:1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)

具体的题目描述如下所示:

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

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

大致的意思就是说明这里的操作是如下所示的:

解释:

就是说,我们要在这个数组中找到几个数字,使他们的和等于我们的目标数,这里的数字的特点就是在数组两端的最外面,类似与剥茧类似,不可以直接使用数组里的数字来和成我们需要的数字;

当然,最后输出的最小操作数就是在满足条件的组合中,选出最少个数的那一组即可,像上面的几种组合中2+3组合的数字最少,所以输出就是2;

例如如下的情况:

当然这里的例子就是力扣上面的题目描述下的举例,那么这里对于这道题的描述就到此为止了,接下来就是如何进行解决这道题;

🚀1.2题目解析

在上述的描述中我们理解到了这道题需要找最少的数字组合为我们需要的目标数,到是如果我们按照题目描述来进行题目的学习,这就麻烦了,这里从那边开始这是不确定的,所以我们就需要重反方向来进行解决:

思路如下:

既然正着做这道题有点难,但是我们可以从反面来进行解决;

1.求可能分散开的数字组合,变为求一定连续的中间的数字组合为数组总和-目标数;

2.求最小的组合数字之和,变为求中间区间满足条件的长度;

3.最后返回时,进行"取反"即可

具体的图示如下:

解释:

此时就变成了典型滑动窗口的问题了,即在满足连续区间为数组总和减去这里目标数的值,最后在求到最大的长度后,用数组总长度减去区间长度即可~~~

所以解决这类问题的时候,要注意变换思路来进行解决,如果按照题目描述这样来进行操作,那么此时就会导致思路"卡死"的情况;当然有这种解法,可以在评论区评论哦;

🚀1.3题目代码

本题的代码如下所示:

java 复制代码
class Solution {
    public int minOperations(int[] nums, int x) {
        //首先定义sum
        int sum=0;
        for(int num :nums){
            sum+=num;
        }
        int target = sum - x;
        if(target < 0){
            return -1;
        }
        int temp=0;
        int ret=-1;
        //开始滑动窗口的操作
        for(int left=0,right=0; right < nums.length;right++){
            temp+=nums[right];
            while(temp > target){
                temp-=nums[left];
                left++;
            }
            //更新条件
            if(target == temp){
                ret=Math.max(ret,right-left+1);
            }
        }
        if(ret == -1){
            return -1;
        }else{
            return nums.length - ret;
        }
    }
}

解释

第一步:计算出这里我们的需要反思维需要的目标数(数组总和--题目目标数target)

第二步:"进窗口"当这里的right向后移动后,不断进行相加的操作,将数字进入窗口

第三步:"判断条件"这里就是判断此时和是否大于目标数

第四步:"出窗口"这里基于判断条件进行操作,大于了此时right向后移动就没有意义了,那么left向后移动,将数字出窗口;

第五步:时刻在满足数字之和等于我们的目标数时进行更新返回的长度,最后根据题意进行放回-1,或者(总长度--目标函数);

🚀1.4代码复杂度

这里由于是一直向前移动,看起来是两个循环嵌套,其实是时间负责度就是O(n),由于前面进行循环进行数组总和的操作所以这里总的时间复杂度是2*O(n),这里的2可以省去,那么最终的时间复杂度就是O(n);

📚️2.水果成篮

🚀2.1题目描述

你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类

你想要尽可能多地收集水果。然而,农场的主人设定了一些严格的规矩,你必须按照要求采摘水果:

  • 你只有 两个 篮子,并且每个篮子只能装 单一类型 的水果。每个篮子能够装的水果总量没有限制。
  • 你可以选择任意一棵树开始采摘,你必须从 每棵 树(包括开始采摘的树)上 恰好摘一个水果 。采摘的水果应当符合篮子中的水果类型。每采摘一次,你将会向右移动到下一棵树,并继续采摘。
  • 一旦你走到某棵树前,但水果不符合篮子的水果类型,那么就必须停止采摘。

给你一个整数数组 fruits ,返回你可以收集的水果的 最大 数目。

上述的话语具体的描述:

这里的树的种类用数字来进行代替,求一段连续的区间,然后我们这里的两个篮子就是代表这个区间里只能包含两种树的类型,即两种数字;然后每棵树采摘一次,求最多的采摘就是求这个区间的长度要尽可能的大~~~

实例如下:

总结:

其实对于这道题来说,最重要的就是读懂这里的题目描述,那么到后面就是使用暴力或者双指针的操作来进行思路的分析;

🚀2.2题目解析

1.暴力枚举

暴力枚举是比较容易想到的一种方法,就是使用双指针来判断这个区间里的数字的种类多少,以及更新区间的长度,如果出现了超过两种类型的数字,那么left+1,然后这里的right就进行=left的操作重新开始;

2.滑动窗口

滑动窗口的具体思路和暴力枚举差不多,但是滑动窗口是对暴力枚举的一种优化,我们判断right是否需要进行前移的操作

思路:

在满足条件时,我们需要保持right进行后移,left与right组成区间里的数字的种类是否大于2,如果大于2,right不用前移动,为啥呢?

此时left+1后,在更新的区间里数字的种类只有两种:

1、种类还是等于3

2、种类等于2

所以此时left必须后移动,直到区间里只有两种类型的数据

图示如下:

解释:

那么此时可以发现有两种情况,此时right不需要进行前移,这是完全没有必要的,只需保持left在满足大于2的条件下向后移动即可;当满足种类小于或者等于2的时候right继续向后移动;

🚀2.3题目代码

1.哈希表

具体的代码如下所示:

java 复制代码
class Solution {
    public int totalFruit(int[] fruits) {
        定义一个hash表
        HashMap<Integer,Integer> hash=new HashMap<>();
        int ret=0;
        //此时进行进入窗口
        for(int left=0,right=0;right < fruits.length ; right++){
            int in=fruits[right];
            hash.put(in,hash.getOrDefault(in,0)+1);
            //长度大于2时
            while (hash.size() > 2){
                int out=fruits[left];
                hash.put(out,hash.get(out)-1);
                if(hash.get(out) == 0){
                    hash.remove(out);
                }
                left++;
            }
            ret=Math.max(ret,right-left+1);
        }
        return ret;
     }
}

解释:

这里使用哈希表来存储,此时分为如下几步

第一步:"进窗口",在满足数的种类小于等于2的情况下进行right后移实现进窗口;这里运用哈希表的去重机制,实现重复的数字的去重,然后更新此数字的数目;

第二步:"判断条件",当种类大于2时,进行出窗口

第三部:"出窗口",将left指针下的数字进行出窗口,但是此时还要保证这个数字的数量为0,才能使right后移,所以不断更新left后移,对不同的数字个数进行减1,最后如果数字的数量为0了,才能说明此时区间里数字的种类为2;

第四步:不断更新这里的长度的最大值,需要设置一个额外的变量,最后循环完成后,返回即可;

2.数组

其实这种方式就是模仿hash表,通过数组下标来表示这里的数字类型,具体的代码如下所示:

java 复制代码
class Solution {
    public int totalFruit(int[] fruits) {
        int n=fruits.length;
        int[] hash=new int[n+1];
        int ret=0;
        //此时进行进入窗口
        for(int left=0,right=0,kinds=0;right < fruits.length ; right++){
            int in=fruits[right];
            //说明这里是没有存在这类的水果
            if(hash[in] == 0){
                kinds++;
            }
            hash[in]++;

            //长度大于2时
            while (kinds > 2){
                int out=fruits[left];
                hash[out]--;
                if(hash[out] == 0){
                    kinds--;
                }
                left++;
            }
            ret=Math.max(ret,right-left+1);
        }
        return ret;
    }
}

解释:

这里就是通过数组模仿哈希表,唯一的改变就是添加了kinds变量来实现种类的数量的表示,只有当此时数组元素还是为0时,才能进行种类的相加,当出窗口时,只有当out索引表示的此类数字的数量等于0时,才会进行kinds--的操作;

🚀2.4代码复杂度

这两种方式的时间复杂度都是O(n),但是都是开辟了另一个空间;

一类时哈希表,HashMap所占用的空间就是和存储的键值对数量相关,其空间复杂度为O(N)量级。

一类就是数组,空间复杂度主要由数组 hash 决定,整体空间复杂度为 ,其中 n 是组 fruits 的长度。

📚️3.总结

本期主要讲解了关于力扣上面的两道题目:1.将x减小到零的最小操作数 ,水果成篮;两篇的核心就是改变思维方式,从反面进行入手,以及如何读懂题目描述,利用滑动窗口进行解决;

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

😊😊 期待你的关注~~~

相关推荐
23级二本计科38 分钟前
排序算法的实现(插入,希尔,选择,冒泡,堆排,快排)
算法·排序算法
Nydia.J1 小时前
【学习笔记】数据结构(十)
数据结构·考研
8Qi81 小时前
多目标优化算法——基于聚类的不规则Pareto前沿多目标优化自适应进化算法(CA-MOEA)
人工智能·算法·多目标优化·进化计算·群体智能·ca-moea
float_六七1 小时前
二叉树的二叉链表和三叉链表
数据结构·链表
Swift社区2 小时前
LeetCode - #180 Swift 实现连续数字查询
算法·leetcode·swift
黑客K-ing2 小时前
如何安全保存用户密码及哈希算法
算法·哈希算法
Allo2022 小时前
数据结构——图
数据结构
最好Tony2 小时前
深度学习blog-Meanshift均值漂移算法-最大熵模型
深度学习·算法·均值算法
麻瓜老宋2 小时前
【手搓一个脚本语言】七、用C语言抽象语法树AST实现一个可交互运行的表达式计算器
c语言·开发语言·数据结构·算法
HUT_Tyne2652 小时前
力扣--844.比较含退格的字符串
算法·leetcode·职场和发展