贪心算法理论基础:由局部最优到全局最优
贪心类题目没有套路,没有连贯性。
455.分发饼干
解题思路:用尺寸大的饼干去喂胃口大的小孩,先将胃口大的孩子喂饱再喂小的。
具体代码:
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
//思路:用尺寸大的饼干去喂胃口大的小孩,先将胃口大的孩子喂饱再喂小的,所以将两数组排序,从尺寸大的饼干开始喂。
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int count = 0;//记录成功满足的孩子的数量
int index = s.size() - 1;
for(int i = g.size() - 1;i >= 0;i--){
//用确定的饼干去匹配小孩的胃口,只有这块饼干匹配到可以满足的小孩的胃口,再去换下一块饼干。
if(index >= 0 && s[index] >= g[i]){//内层循环因为只有成功满足一个小孩,才能进入循环计数,for循环相当于是一个无限循环,条件不太好控制又是单次循环所以用if就足够了。
count++;
index--;
}
}
return count;
}
};
376.摆动序列
解题思路:
如题意所说,上题的摆动序列应该是三个摆动,本题让我们求最长的摆动序列,如果中间有单调坡或平坡就删掉。但是我们在真正实现的过程中多次遍历数组并删掉其中的值是在时间上耗费较大的,题目既然只让我们求最长的摆动序列的长度,我们就可以在数组遍历到峰值处时将计数器加1就可以了,没必要去真正的删除元素,所以思路如果选的不对就很容易陷入僵局。
然后我们来考虑一下摆动序列计数的几种情况,第一种情况就是有平坡,有平坡但是我们又不真的删除元素,那么就是当摆动序列中有一个数为0的时候,他的前一个值和后一个值一定不为0。第二中情况就是首尾元素,如果整个序列中只有两个元素并且不知道两个元素是否相等,但是prediff和curdiff的计算至少需要三个元素,这时候我们可以将序列中右边的元素定为一个序列,左边的作为初始元素 i ,在i的前面添加一个与 i 相等的i-1,这样就可以计算出摆动长度了。
在遍历数组时,i每次加加,都要重算一次curdiff但是prediff不用,i移动prediff就移动到原先的curdiff的位置。第三种情况就是在单调坡中有平坡,如果整个序列是一个单调坡,那他的摆动长度就是2,也就是在初始坡度上的两个元素。但是有平坡可能就会影响prediff的值,所以我们可以在出现摆动的时候去更新prediff的值,也就是使prediff只记录摆动的初始值。
具体代码如下:
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if(nums.size() == 1) return 1;
int result = 1;//默认数组最右端有一个坡度
int prediff = 0;
int curdiff = 0;
for(int i = 0;i < nums.size() - 1;i++){
curdiff = nums[i + 1] - nums[i];
if(curdiff > 0 && prediff <= 0 ||curdiff < 0 && prediff >= 0){
result ++;
prediff = curdiff;
}
}
return result;
}
};
53.最大子序和
解题思路:暴力解法就是用两层for循环,但是时间复杂度为O(n ^ 2),超出时间限制了。
贪心解法:局部最优就是当我们遍历数组时遍历到连续和为负数的时候,我们就放弃前面的连续和,从下一个数开始,全局最优就是找到这个连续的子序列。
具体代码:
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int result = INT_MIN,sum = 0;
for(int i = 0;i < nums.size();i++){
sum += nums[i];
if(sum > result){//必须先更新result,再选择是否从下一个元素开始
result = sum;
}
if(sum <= 0){
sum = 0;
}
}
return result;
}
};