题目:455.分发饼干、376. 摆动序列、53. 最大子序和
参考链接:代码随想录
理论基础
用我自己的话说,贪心算法通俗一点就是一条路走到黑,每次都选择局部最优解,最后确保全局也是最优情况。即局部最优必定推出整体最优,也没有固定的套路。具体做的时候简单想一下就可以了,不需要数学推导。
455.分发饼干
思路:记住,贪心算法没有固定套路,有时候根据常识思考就可以!对本题,首先想对孩子胃口和饼干都从小到大排序,对小胃口,先用小饼干满足,如果小饼干不能满足就换大的饼干,即先用尽可能小的饼干满足尽可能小的孩子,这样能在最节约饼干的基础上满足更多的孩子。相反,如果一开始就把最大的饼干用在了最小的孩子上,那么肯定不行了。在具体实现上就是一个双指针法。时间复杂度O(nlogn),主要是排序。
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int ans=0;
int i=0,j=0;//双指针
while(i<g.size()&&j<s.size()){
if(s[j]>=g[i]){
ans++;
i++;
j++;
}
else{//饼干小了
j++;
}
}
return ans;
}
};
看了标答发现,先用大饼干满足大胃口孩子也是可以的。
标答:
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(), g.end());
sort(s.begin(), s.end());
int index = s.size() - 1; // 饼干数组的下标
int result = 0;
for (int i = g.size() - 1; i >= 0; i--) { // 遍历胃口
if (index >= 0 && s[index] >= g[i]) { // 遍历饼干
result++;
index--;
}
}
return result;
}
};
376. 摆动序列
思路:本题没有思路,直接看解答。
如图,我们只需要删除坡度上的节点,保持峰值尽可能多,这就是局部最优推出全局最优。而本题只要求长度,也不用删除,直接统计峰值数量。
对具体情况还得分类讨论。首先是上下坡中有平坡,这时候可以将左边的几个都删掉只留右边一个;第二种情况是数组首尾两端的统计,因为我们统计峰值靠的是两边差值,至少需要三个数才能够计算,这里可以假设左边有一个一样的数,一开始默认result为1;情况三是单调中的平坡,我们不能每次都修改preDiff,而要在坡度发生变化的时候才能更新。时间复杂度O(n)。
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
if (nums.size() <= 1) return nums.size();
int curDiff = 0; // 当前一对差值
int preDiff = 0; // 前一对差值
int result = 1; // 记录峰值个数,序列默认序列最右边有一个峰值
for (int i = 0; i < nums.size() - 1; i++) {
curDiff = nums[i + 1] - nums[i];
// 出现峰值
if ((preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)) {
result++;
preDiff = curDiff; // 注意这里,只在摆动变化的时候更新prediff
}
}
return result;
}
}
我们看完答案后再反过来理解这段代码,首先是一开始将答案至为1,当数组长度大于2且第一个是坡度时,直接就变成2了。对curDiff的判断,只有为正或者负,如果为0则不考虑,而preDiff只要与curDiff相反(平也是相反),那么就会更新答案。
本题还是很难直接想到的。DP方法目前看不懂。
53. 最大子序和
思路:本题要求连续,一开始很容易想到的就是暴力方法。即两层for循环,前一个遍历开始位置,后一个计算和。时间复杂度O(n^2)。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans=INT_MIN;
int sum;
for(int i=0;i<nums.size();i++){
sum=0;
for(int j=i;j<nums.size();j++){
sum+=nums[j];
ans=sum>ans?sum:ans;
}
}
return ans;
}
};
直接超出时间限制了,故这个方法实际上不可取。
考虑贪心算法,直接看解答。即前面的连续和为负数的时候,直接舍弃,从下一个元素开始计算,因为负数只会拉低连续和。最后全局最优即选择最大的连续和。时间复杂度O(n)。
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int ans=INT_MIN;
int sum=0;
for(int i=0;i<nums.size();i++){
sum+=nums[i];
if(sum>ans){//更新ans
ans=sum;
}
if(sum<0){//连续和负数,直接置0,从下一个开始考虑
sum=0;
}
}
return ans;
}
};
看代码感觉非常容易,实际上贪心的点并不好想到。
DP先跳过。