文章目录
- 前言
- 一、理论基础
- 二、455.分发饼干
- 三、376. 摆动序列
- 四、53. 最大子序和
- 总结
前言
贪心。
一、理论基础
贪心没有套路,说白了就是常识性推导加上举反例。
贪心算法一般分为如下四步:
- 将问题分解为若干个子问题
- 找出适合的贪心策略
- 求解每一个子问题的最优解
- 将局部最优解堆叠成全局最优解
二、455.分发饼干
贪心按照大胃口和小胃口分发,如下:
这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。也可以换一个思路,小饼干先喂饱小胃口。
另外,胃口和尺寸循环的顺序也需要考虑。
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int count = 0;
int start = s.length - 1;
for(int index = g.length-1;index>=0;index--){
if(start >= 0 && g[index] <= s[start]){
start--;
count++;
}
}
return count;
}
}
class Solution{
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]){
count++;
start++;
}
}
return count;
}
}
三、376. 摆动序列
局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。
整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。
局部最优推出全局最优,并举不出反例,那么试试贪心!
(为方便表述,以下说的峰值都是指局部峰值)
实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)
这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点
在计算是否有峰值的时候,大家知道遍历的下标 i ,计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i]),如果prediff < 0 && curdiff > 0
或者 prediff > 0 && curdiff < 0
此时就有波动就需要统计。
这是我们思考本题的一个大题思路,但本题要考虑三种情况:
- 情况一:上下坡中有平坡
- 情况二:数组首尾两端
- 情况三:单调坡中有平坡
提议没有绊住me,反到是 curDiff = num[i] - nums[i-1];理解了很久,因为i是从1开始的,所以num[i-1]是num[0]开始的,也就是第一个,这也是平坡的缘由。
class Solution {
public int wiggleMaxLength(int[] nums) {
if(nums.length <= 1){
return nums.length;
}
int curDiff = 0;
int preDiff = 0;//这里默认前面是平坡,也就是和第一个的值相等的
int count = 1;
for(int i = 1;i<nums.length;i++){
curDiff = nums[i] - nums[i-1];
if((curDiff > 0 && preDiff <= 0) || (curDiff < 0 && preDiff >= 0)){
count++;
preDiff = curDiff;
}
}
return count;
}
}
四、53. 最大子序和
贪心贪的是哪里呢?
如果 -2 1 在一起,计算起点的时候,一定是从 1 开始计算,因为负数只会拉低总和,这就是贪心贪的地方!
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
局部最优的情况下,并记录最大的“连续和”,可以推出全局最优。
从代码角度上来讲:遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。
这相当于是暴力解法中的不断调整最大子序和区间的起始位置。
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 1){
return nums[0];
}
int sum = Integer.MIN_VALUE;
int count = 0;
for(int i = 0;i < nums.length;i++){
count += nums[i];
if(count > sum){
sum = count;
}
if(count < 0){
count = 0;
}
}
return sum;
}
}
总结
突破口在于:找到贪心在哪里?