算法入门:专题攻克二---滑动窗口3(将x减到0的最小操作数,水果成篮)
🎬 胖咕噜的稞达鸭:个人主页

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

- 题目解析:
在整数数组nums中,每当移除数组最左边或者最右边的元素,就要从x中减去该元素的值,将x减到0,并且返回最小的操作数字,也就是从原数组中移除了多少个数字可以将x减到0,移除的数字越少就返回,改动原数组改动最小的返回。
给一个例子:nums=[ 1 , 1 , 4 , 2 , 3 ] ,此时x=5,移除数组索引为0的元素1,x-1=4,x现在还没被减到0,移除前两个元素和最后一个元素,x=0,此时改动了3个元素,返回3;还可以移除后两个元素,x = 0,此时只改动了两个元素,返回2.题目要求要返回最小的操作数,此时返回2.
如果数组中找不到可以使得x减到0的(如数组:nums = [ 5 , 6 , 7 , 8 , 9 ],返回-1即可。
- 算法原理:
同一个数组,操作数最小,x减去的数字越少就满足题目的要求,x中减去的数字越少也就意味着留下来的数字越多,也就是说最后比较的是,返回ret,ret的最大值即可。可以将所有数字都相加,最终结果放到sum中,如果所有元素的和等于sum-x,就可以计算此时最长的子数组的长度len.
可以先用暴力枚举的方法,用双指针left,right从数组索引为0的位置开始遍历,right从数组索引为0的位置开始向后,每遍历一个数字加到Add中。当Add大于sum-x的时候,意味着left该出窗口了,如果Add等于sum-x的时候,就是我们想要的结果,将ret更新,从更新前的ret到right-left+1的ret选取大的进行更新。最后返回的是数组的元素个数减去ret,本身要求的就是返回操作的最小数字。ret中的数字越多,操作的数字就越少。
还有一种情况:如果数组中找不到可以将x减为0的组合,返回-1.此时可以先将ret置为-1,初始化的时候置为-1,等到数组中有可以将x减为0的组合,再更新ret,这也是一个小细节。
- 现在来写代码。
第一步:
将数组中所有数字相加sum,我们要找的组合一定是等于sum-x ;
第二步:初始化ret,如果数组中没有组合可以将x减为0,返回-1;有就执行下面的操作;
第三步:定义双指针,进入窗口(当right一个一个遍历加到Add中,Add<sum-x);出窗口(当Add > sum-x,此时left需要向后移动一个位置,表示出窗口);再次判断,此时的left,right的区间内数字相加是不是Add==sum-x。如果是就更新ret,ret=right-left+1和ret(原始)的最大值。
第四步:返回。
class Solution {
public:int minOperations(vector<int>& nums, int x) {int sum=0;for(int a:nums) sum+=a;int target=sum-x;if(target<0) return -1;int ret=-1;for(int left=0,right=0,Add=0;right<nums.size();right++){Add+=nums[right];while(Add>target) {Add -= nums[left++]; }if(Add == target) {ret =max(ret,right-left+1);}}if(ret == -1)return ret=-1;else {return nums.size()-ret;}}
};
水果成篮
水果成篮

解法一:
哈希表代替数组,申请足够大的空间,并将其初始化为0,最终要返回的是最长的子数组,这个子树组的特点是其中只有两个类型的数字,也就是他题目规定的只有两个篮子;
两个篮子用kinds代替;滑动窗口,进入窗口的操作,如果right的数字在哈希表中==0,kinds++,代表哈希表中多了一种水果,同时right++进入数组;
出窗口,当kinds>2,left走到同一种水果的数量和种类都为零的时候,再向后移动一个位置,此时判断数组的长度,ret不断更新最大值。
class Solution {
public:int totalFruit(vector<int>& f) {int hash[1000001]={0};int ret=0;for(int left=0,right=0,kinds=0;right<f.size();right++){if(hash[f[right]] == 0)kinds++;hash[f[right]]++;while(kinds>2){ hash[f[left]]--;if(hash[f[left]]==0)kinds--;left++;}ret=max(ret,right-left+1);}return ret;}
};
解法二:创建一个哈希表,因为哈希表中总共存储的数字类型只有两个,所以进入数组的hash.size() 一旦大于2,就必须出窗口,出窗口的left必须走完所有类型,直到最后只剩下right一种类型,一旦在哈希表中hash[f[left]]==0,删除f[left],最后要让left走到新的的位置。还要判断此时的ret.更新一下结果。
class Solution {
public:int totalFruit(vector<int>& f) {unordered_map<int,int>hash;int ret=0;for(int left=0,right=0;right<f.size();right++){hash[f[right]]++;while(hash.size()>2){hash[f[left]]--;if(hash[f[left]]==0)hash.erase(f[left]);left++;}ret=max(ret,right-left+1);}return ret;}
};
