题目1:455 分发饼干
题目链接:分发饼干
对题目的理解
每个孩子最多只能有1块饼干,g[i]是每个孩子满足胃口的最小饼干尺寸,每个饼干的尺寸是s[j],s[j]>=g[i],满足尽可能多的孩子,至少有一个孩子,至少有0块饼干。
贪心算法
法一:大饼干优先喂大孩子
为了满足更多的小孩,大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的,使用大饼干满足胃口大的孩子。
局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
先将饼干数组和小孩数组排序。然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量
代码
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 sum = 0;
for(int i=g.size()-1;i>=0;i--){
if(index>=0 && s[index]>=g[i]){
sum++;
index--;
}
}
return sum;
}
};
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
用了一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式,这也是常用的技巧
!!!注意不可以先遍历饼干再遍历胃口,因为有可能胃口很大,超过了饼干的尺寸,那么内层因为不满足if条件,胃口一直停留在最大的那里,而饼干尺寸一直向前移,这样得出的结果是错误的
反例:饼干一直动,胃口不动
法二:小饼干先喂小胃口
遍历顺序是先遍历的饼干,再遍历的胃口
如果先遍历胃口,再遍历饼干,那么可能出现,饼干一直不满足条件,一直比胃口小,那么会出现胃口一直动(会越来越大),饼干不动的现象
反例 胃口一直动,饼干不动
代码
class Solution {
public:
int findContentChildren(vector<int>& g, vector<int>& s) {
sort(g.begin(),g.end());
sort(s.begin(),s.end());
int index = 0;
int sum = 0;
for(int i=0;i<s.size();i++){
if(index<g.size() && s[i]>=g[index]){
sum++;
index++;
}
}
return sum;
}
};
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)
题目2:376 摆动序列
题目链接:摆动序列
对题目的理解
连续数字之间的差在正数和负数之间交替出现,称为摆动序列;仅含一个元素或两个不等元素的序列也是摆动序列;子序列可以从原始序列中删除(或不删除)元素获得,元素的顺序不变
返回整数数组中摆动序列中的最长子序列的长度,数组中至少包含一个元素。
贪心算法
局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。
整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。
没有必要真的删除数组中的元素,遇到摆动时,做一个加加的操作即可,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)
这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点。
如何判断摆动条件
本题要考虑三种情况:
1.情况一:上下坡中有平坡
2.情况二:数组首尾两端
3.情况三:单调坡中有平坡
只需要在 这个坡度 摆动变化的时候,更新 prediff 就行,这样 prediff 在 单调区间有平坡的时候 就不会发生变化,造成我们的误判。
伪代码
代码
class Solution {
public:
int wiggleMaxLength(vector<int>& nums) {
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((prediff>=0 && curdiff<0)||(prediff<=0 && curdiff>0)){
result++;
prediff = curdiff;
}
}
return result;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
动态规划
本题也可以使用动态规划的方法
题目3:53 最大子序列和
题目链接:最大子序列和
对题目的理解
找出整数数组中具有最大和的连续子数组(至少包含一个元素),返回最大值。
!!!注意题目说的是连续和,注意连续,注意连续,注意连续!!!
贪心算法
连续和为负数,直接从下一个数字计算,若负数加上下一个数则定会让下一个数变小,所以干脆就不加前面的连续和了,从当前位置的数重新算起。
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小;只要连续和还是正数就会 对后面的元素 起到增大总和的作用。 所以只要连续和为正数我们就保留。
全局最优:选取最大“连续和”
局部最优的情况下,并记录最大的“连续和”,可以推出全局最优。
图中黑色箭头表示重新开始遍历
伪代码
代码
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int count=0;//记录连续和
int result = INT_MIN;//记录最大连续和
for(int i=0;i<nums.size();i++){
count += nums[i];
if(count>result) result = count;
if(count<0) count = 0;//连续和为负数,重新计算连续和
}
return result;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(1)
动态规划
本题也可以使用动态规划求解