代码随想录算法训练营第三十一天|LeetCode455,376,53
Day31
理论基础
贪心最大的难处在于:不知道哪里用了贪心
有的时候题目做出来了,还不知道自己用了贪心。
贪心的本质是选择每一阶段的局部最优,从而达到全局最优。
有同学问了如何验证可不可以用贪心算法呢?
最好用的策略就是举反例,如果想不到反例,那么就试一试贪心吧。
455.分发饼干 (排序算法的实现)
看到题的想法是按照升序对两个数组进行排序
然后判断满足值g[i]是否小于等于饼干s[j],如果满足条件就将i,j后移,否则,仅仅移动j找一个更大的饼干。
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int i = 0;
int j = 0;
while (i<g.length&&j<s.length){
if (g[i]<=s[j]){
i++;
j++;
}else if (g[i] > s[j]){
j++;
}
}
return i;
}
376.摆动序列 (*)
一点思路都没有
局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。
整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列。
局部最优推出全局最优,并举不出反例,那么试试贪心!
本题要考虑三种情况:
- 情况一:上下坡中有平坡
- 情况二:数组首尾两端
- 情况三:单调坡中有平坡
public int wiggleMaxLength(int[] nums) {
if(nums.length<1){
return nums.length;
}
//当前差值
int curDiff = 0;
//前一个差值
int preDiff = 0;
//记录峰值个数,默认为序列最右边有一个峰值是为了处理数组只有两个值的情况。
int result = 1;
for(int i = 0; i < nums.length - 1;i++){
//得到当前值
curDiff = nums[i+1] - nums[i];
//出现峰值。这里preDiff = 0是考虑出现平坡的时候去除左边的值,以及仅仅有两个数值的情况
if ((preDiff<=0&&curDiff>0)||(preDiff>=0&&curDiff<0)){
result++;
//更新preDiff的时机要注意,只有当出现摆动(峰值)的时候才更新preDiff。这样就处理了在单调过程中出现平坡的问题
preDiff = curDiff;
}
}
return result;
}
53.最大子序和
贪心贪的是哪里呢?
如果 -2 1 在一起,计算起点的时候,一定是从1开始计算,因为负数只会拉低总和,这就是贪心贪的地方!
局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小。
全局最优:选取最大“连续和”
局部最优的情况下,并记录最大的“连续和”,可以推出全局最优。
从代码角度上来讲:遍历nums,从头开始用count累积,如果count一旦加上nums[i]变为负数,那么就应该从nums[i+1]开始从0累积count了,因为已经变为负数的count,只会拖累总和。
这相当于是暴力解法中的不断调整最大子序和区间的起始位置。
第一次错误:如果传入的数值全部是负数。
改正:将 result = count > result?count:result;放到count+=nums[i]后,这样可以保证负数也会有正常的返回值。
public int maxSubArray(int[] nums) {
int result = Integer.MIN_VALUE;
int count = 0;
for (int i = 0; i < nums.length; i++){
count += nums[i];
result = count > result?count:result;
//贪心的地方,如果连续和小于0了,那么加上下一个元素只会变小,所以应该从下一个值开始重新计算count
if (count < 0){
count = 0;
}
}
return result;
}