贪心算法01
- 理论基础
- 455.分发饼干
- 解题思路
- 376. 摆动序列
- 解题思路
- 拓展
- 53. 最大子序和
- 解题思路
- 常见误区
- 注意点
贪心算法其实就是没有什么规律可言,所以大家了解贪心算法 就了解它没有规律的本质就够了。
不用花心思去研究其规律, 没有思路就立刻看题解。
基本贪心的题目 有两个极端,要不就是特简单,要不就是死活想不出来。
学完贪心之后再去看动态规划,就会了解贪心和动规的区别。
理论基础
理论基础
455.分发饼干
题目链接: 455.分发饼干
文章讲解/视频讲解: 455.分发饼干
解题思路
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。
可以尝试使用贪心策略,先将饼干数组和小孩数组排序。
然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量。
// 贪心 优先考虑胃口,先喂饱胃口大的
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int result = 0;
int index = s.length - 1;
for(int i = g.length - 1; i >=0; i--) {// 必须要先遍历饼干
if(index >= 0 && s[index] >= g[i]){ // 遍历孩子
index--;
result++;
}
}
return result;
}
}
// 贪心 小饼干先喂饱小胃口
// 贪心 先遍历的饼干,再遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。
class Solution {
// 思路1:优先考虑饼干,小饼干先喂饱小胃口
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int start = 0;
int count = 0;
for (int i = 0; i < s.length && start < g.length; i++) {
if (s[i] >= g[start]) {
start++;
count++;
}
}
return count;
}
}
376. 摆动序列
题目链接: 376. 摆动序列
文章讲解/视频讲解: 376. 摆动序列
解题思路
实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)
本题要考虑三种情况:
-
情况一:上下坡中有平坡
统一规则,删除左边的三个 2
所以我们记录峰值的条件应该是: (preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0),
-
情况二:数组首尾两端
如果只有两个不同的元素,那摆动序列也是 2。
例如序列[2,5],如果靠统计差值来计算峰值个数就需要考虑数组最左面和最右面的特殊情况。
因为我们在计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i])的时候,至少需要三个数字才能计算,而数组只有两个数字。
可以假设,数组最前面还有一个数字
-
情况三:单调坡中有平坡
如果只考虑前两种情况,那么我们在三个地方记录峰值,但其实结果因为是 2,因为 单调中的平坡 不能算峰值(即摆动)。
之所以会出问题,是因为我们实时更新了 prediff。
那么我们应该将更新 prediff的代码包括进if中
我们只需要在 这个坡度 摆动变化的时候,更新 prediff 就行,这样 prediff 在 单调区间有平坡的时候 就不会发生变化,造成我们的误判。
拓展
用动态规划来做,还没看,代码随想录上有,后面再看
// 贪心
class Solution {
public int wiggleMaxLength(int[] nums) {
if(nums.length <= 1){
return nums.length;
}
int preDiff = 0;
int curDiff = 0;
int count = 1; // 最后一个元素算一个
for(int i = 1; i < nums.length; i++){
curDiff = nums[i] - nums[i-1];
if((preDiff>=0 && curDiff<0) || (preDiff<=0 && curDiff>0)){
count++;
preDiff = curDiff;
}
}
return count;
}
}
53. 最大子序和
题目链接: 53. 最大子序和
文章讲解/视频讲解: 53. 最大子序和
解题思路
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”。
常见误区
== Integer.MIN_VALUE问题==
sum和sumMax 要初始化为最小负数。
要设为intsum = Integer.MIN_VALUE
;
因为如果输入用例都是-1,或者 都是负数,假设都是-1的情况下,正确结果应该是-1,
int sum = Integer.MIN_VALUE,这个贪心算法跑出来的结果是 -1,结果正确
而若int sum = 0,这个贪心算法跑出来的结果是 0,结果错误
更新起始位置的条件
前面元素的连续和为负数,我们才更新sum=0,从当前元素重新开始累加。(即 我们要看前面那些元素的累加和对我们当前这个元素有没有贡献)
不必担心更新过程sumMax记录的值不是最大值,因为sumMax是和sum实时比较更新的。
注意点
Integer.MIN_VALUE问题
要设为int sum = Integer.MIN_VALUE;
设为int sum = 0;有些测试案例通不过
// 贪心
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 1){
return nums[0];
}
int sumMax = Integer.MIN_VALUE;
int sum = Integer.MIN_VALUE;
for(int i = 0; i< nums.length; i++){
if(sum < 0){
sum = 0;
}
sum += nums[i];
sumMax = Math.max(sum, sumMax);
}
return sumMax;
}
}