代码随想录算法训练营第31天(贪心算法01 | ● 455.分发饼干 ● 376. 摆动序列 ● 53. 最大子序和

贪心算法01

贪心算法其实就是没有什么规律可言,所以大家了解贪心算法 就了解它没有规律的本质就够了。
不用花心思去研究其规律, 没有思路就立刻看题解。
基本贪心的题目 有两个极端,要不就是特简单,要不就是死活想不出来。
学完贪心之后再去看动态规划,就会了解贪心和动规的区别。

理论基础

理论基础

455.分发饼干

题目链接:455.分发饼干

文章讲解/视频讲解:455.分发饼干

解题思路

这里的局部最优就是大饼干喂给胃口大的,充分利用饼干尺寸喂饱一个,全局最优就是喂饱尽可能多的小孩。

可以尝试使用贪心策略,先将饼干数组和小孩数组排序。

然后从后向前遍历小孩数组,用大饼干优先满足胃口大的,并统计满足小孩数量。

java 复制代码
// 贪心  优先考虑胃口,先喂饱胃口大的
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;
    }
}
java 复制代码
// 贪心  小饼干先喂饱小胃口
// 贪心  先遍历的饼干,再遍历的胃口,这是因为遍历顺序变了,我们是从小到大遍历。
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. 摆动序列

解题思路

实际操作上,其实连删除的操作都不用做,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)

本题要考虑三种情况:

  1. 情况一:上下坡中有平坡

    统一规则,删除左边的三个 2

    所以我们记录峰值的条件应该是: (preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0),

  2. 情况二:数组首尾两端

    如果只有两个不同的元素,那摆动序列也是 2。

    例如序列[2,5],如果靠统计差值来计算峰值个数就需要考虑数组最左面和最右面的特殊情况。

    因为我们在计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i])的时候,至少需要三个数字才能计算,而数组只有两个数字。

    可以假设,数组最前面还有一个数字

  3. 情况三:单调坡中有平坡

    如果只考虑前两种情况,那么我们在三个地方记录峰值,但其实结果因为是 2,因为 单调中的平坡 不能算峰值(即摆动)。

    之所以会出问题,是因为我们实时更新了 prediff。

    那么我们应该将更新 prediff的代码包括进if中

    我们只需要在 这个坡度 摆动变化的时候,更新 prediff 就行,这样 prediff 在 单调区间有平坡的时候 就不会发生变化,造成我们的误判。

拓展

用动态规划来做,还没看,代码随想录上有,后面再看

java 复制代码
// 贪心
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;有些测试案例通不过

java 复制代码
// 贪心
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;
    }
}
相关推荐
异常君3 分钟前
MySQL 中 count(*)、count(1)、count(字段)性能对比:一次彻底搞清楚
java·mysql·面试
sduwcgg27 分钟前
python的numpy的MKL加速
开发语言·python·numpy
wkj00130 分钟前
QuaggaJS 配置参数详解
java·linux·服务器·javascript·quaggajs
水蓝烟雨31 分钟前
[面试精选] 0094. 二叉树的中序遍历
算法·面试精选
钢铁男儿36 分钟前
Python 接口:从协议到抽象基 类(定义并使用一个抽象基类)
开发语言·python
超闻逸事37 分钟前
【题解】[UTPC2024] C.Card Deck
c++·算法
暴力求解1 小时前
C++类和对象(上)
开发语言·c++·算法
JKHaaa1 小时前
几种简单的排序算法(C语言)
c语言·算法·排序算法
让我们一起加油好吗1 小时前
【基础算法】枚举(普通枚举、二进制枚举)
开发语言·c++·算法·二进制·枚举·位运算
大锦终1 小时前
【C++】特殊类设计
开发语言·c++