学习算法,继续加油!!!
一、将 x 减到 0 的最小操作数
题目解析
来看这一道题,题目给定一个数组nums
和一个整数x
;我们可以在数组nums
的左边或者右边进行操作(x
减去该位置的值);如果x
可以减到0
就返回最小操作数;如果不能,就返回-1
。
题目描述很简单,但是我们看到操作可以说非常头大,因为它可以在左边进行操作也可以在右边进行操作,那我们该如何去解决这个问题呢?
这里如果按题目要求去直接找左右两边的最小操作数,那这道题就非常难了。
我们不妨翻过来想,它们要求我们找左右两边操作数最小的;那我们是不是可以找数组中被我们删除左右两边后剩下的部分。
什么意思呢?
如上图所示,[0 , left)
和[right , n)
是我们找到的操作数最小时的左右区间,此时这两个区间的和为x
;那么我们就可以发现[left , right)
区间的和就是sum - x
(其中sum
是数组中所以数据的和)。
那这样我们可以发现,我们找到满足题目中的要求左右两侧的区间时,此时数组中剩余的部分是一段连续的区间
,并且该区间的和为sum - x
。
这时我们发现,只要找到一段连续的区间[left , right)
,并且该区间的和为sum - x
,此时两侧区间的和就是x
,就满足了题意;
那题目中要求我们找到最小的操作数,转换过来就是寻找的区间[left , right)
最长。
算法思路
通过分析题目,我们将题目中的要求反过来想,就是我们需要找到一段连续的区间,此区间的和为sum - x
,我们要找的长度最长的。
现在这道题就转换成了,找到和为
sum - x
的长度最长的区间(子数组
)。
暴力解法:
这里简单提一下暴力解法,暴力解法节枚举所以子数组,找到其中满足条件且长度最长的。
这里暴力解法中right
再从left
位置开始遍历有些冗余,我们对其进行优化,使用s
记录区间[left , right)
的和,这样left++
时,s
减去即可;
这里因为right
遍历到这个位置,区间的和s
刚好满足,那left++
后区间内的和一定小于sum - x
,所以就不必要让right
再从left
位置进行遍历了。
这里使用滑动窗口进行优化:
这里为了方便描述,我们定义一个
target = sum - x
;
- 进窗口:当区间内的和
s < target
时,进窗口,更新区间内的和。- 出窗口:当区间内的和
s > target
时,进行出窗口操作,并更新区间内的和。- 更新结果:当出窗口操作之后,如果区间内的和
s == target
,这是区间满足条件,更新结果。
代码实现
这里写代码有一个坑:如果我们数组中的和小于
x
,此时target
是小于0
的,这时候我们是不能进行滑动窗口的一系列操作的。我们需要进行特殊处理,如果
target < 0
,直接返回-1
即可。
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;
for(int left = 0, right = 0, s = 0;right < nums.size();)
{
s+=nums[right++];
while(s>target)
s-=nums[left++];
if(s == target)
ret = max(ret, right - left);
}
if(ret == -1)
return ret;
else
return nums.size() - ret;
}
};
二、水果成篮
题目解析
初看题目,一种当初做阅读理解的感觉,题目有点长;
来细看这一道题,给我们一个数组fruits
,其中代表每一颗果树的种类;我们有两个果篮(每一个果篮只能装一种水果)我们现在要做的是进行采摘水果,根据题目描述:(我们采摘的水果只能有两种,并且采摘水果时是连续采摘的);我们要求的是我们收集水果的最大数量。
算法思路
了解了题目,现在来看如何去解决:
这里就不拐弯抹角了,直接看解题思路:
- 入窗口:将
right
位置的水果采摘下来,并统计种类和数量。- 出窗口:当采摘的种类
> 2
时,我们要进行出窗口操作,将left
位置对应的水果去出来;直到果篮中的种类<= 2
;- 更新结果:当果篮中的种类
==2
时,更新最终结果即可。
现在问题来了,如何去存储水果的种类和数量,并且我们还要方便进行存储、修改和删除操作?
这里就要用到哈希
结构的unordered_map
了;可以参考 unordered_set和unordered_map。
代码实现
治理代码实现就比较简单了:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int n = fruits.size();
int left = 0, right = 0;
unordered_map<int,int> ump;
int ret = 0;
while(right < n)
{
//进窗口,直接使用[]
ump[fruits[right++]]++;
//出窗口
while(ump.size() > 2)
{
ump[fruits[left]]--;
if(ump[fruits[left]] == 0)
ump.erase(fruits[left]);
left++;
}
ret = max(ret, right - left);
}
return ret;
}
};
到这里,本篇文章算法讲解就结束了,感谢支持。
我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws