代码随想录DAY27|贪心算法 455.分发饼干、376. 摆动序列、53. 最大子序和

1. 贪心算法

贪心算法的思路是找到局部最优解,然后求出全局最优解有固定模板。

2. 分发饼干

力扣
要先遍历需求g[i],让其自减,如果满足不了就比较下一个。

有两种思路,都需要先对两个数组进行排序。一种思路是从后往前遍历,让大尺寸的饼干先满足大胃口的,如果满足不了就到下一个,如果可以满足就将遍历尺寸的Index减一。
在这里插入图片描述
代码实现

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int count = 0;
        int end = s.length - 1;
        for(int i = g.length - 1; i >= 0; i--){
            if(end >= 0 && s[end] >= g[i]){
                count++;
                end--;
            }
        }
        return count;
    }
}

另一种思路是从前向后遍历,在力扣上该方法用时较少。这个就要先遍历尺寸,如果该尺寸满足不了当前需求就向后找直到满足,然后再处理下一个需求。

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int count = 0;
        int start = 0;
        for(int i = 0; i < s.length; i++){
            if(start < g.length && s[i] >= g[start]){
                count++;
                start++;
            }
        }
        return count;
    }
}

3. 摆动序列

力扣

首先要理解本题:子序列是从原序列的任意位置删除元素,相当于不一定是原序列中连续的元素。用以下思路来理解如何删除元素:
在这里插入图片描述
删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值。而对全局使用这样的删除操作,就可以得到最长摆动子序列。本题并未要求删除,只用求出长度即可。思路是从第二个开始计数,在计算是否有峰值的时候,大家知道遍历的下标 i ,计算 prediff(nums[i] - nums[i-1]) 和 curdiff(nums[i+1] - nums[i]),如果prediff < 0 && curdiff > 0 或者 prediff > 0 && curdiff < 0 此时就有波动就需要统计。具体要考虑三种情况

  1. 情况一:上下坡中有平坡
    如下图数组[1,2,2,2,2,1]。
    在这里插入图片描述

要删除前面三个或者后面三个2。如果按照原来的判断峰值方式会出错。在图中,当 i 指向第一个 2 的时候,prediff > 0 && curdiff = 0 ,当 i 指向最后一个 2 的时候 prediff = 0 && curdiff < 0。如果我们采用,删左面三个 2 的规则,那么 当 prediff = 0 && curdiff < 0 也要记录一个峰值,因为他是把之前相同的元素都删掉留下的峰值。所以我们记录峰值的条件应该是: (preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0),为什么这里允许 prediff == 0 ,就是为了 上面我说的这种情况。
2. 情况二:数组首尾两端
计算峰值至少需要三个节点,才能确定中间节点是否记录,那么数组的头尾节点如何处理计数?如果数组长度为2能不能统一到上述规则中?
前文已规定过:一端平一段上下坡也算峰值。那么最左端的开始节点可以默认为前面有一个等值的节点,这样就能把头节点算上。对于尾节点默认为一个峰值(count从1开始算,在遍历过程中如果curdiff=0不会记录,所以不用担心最后一个元素和倒数第二个元素相同)。这样两个节点的数组可以不加修改就整合到统一规则中。
在这里插入图片描述
3. 情况三:单调坡中有平坡
如果实时更新prediff,即把当前的curdiff赋值给prediff用于下一轮遍历,会存在以下问题:单调坡中如果存在平坡,会因为prediff由>0改为=0造成中间的节点也被判为峰值。因此正确的做法是只有当峰值发生变化的时候再改变prediff,开始的时候初始化为0(根据情况二分析)。
在这里插入图片描述
因此最终代码如下

class Solution {
    public int wiggleMaxLength(int[] nums) {
        if(nums.length == 1){
            return 1;
        }
        int prediff = 0;
        int curdiff = 0;
        int count = 1;
        for(int i = 0; i < nums.length - 1; i++){
            curdiff = nums[i + 1] - nums[i];
            if(prediff >= 0 && curdiff < 0 || prediff <= 0 && curdiff > 0){
                count++;
                prediff = curdiff;
            }
        }
        return count;
    }
}

4. 最大子序和

力扣
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
注意:不是遇到复数就从0开始计数count,因为下一个可能是正数把最大和给加回来甚至更高。

首先定义一个最大值,实时更新这个最大值。用count记录当前子序的和。遍历 nums,从头开始用 count 累积,如果 count 一旦加上 nums[i]变为负数,那么就应该从 nums[i+1]开始从 0 累积 count 了,因为已经变为负数的 count,只会拖累总和。如果count>最大值,及时记录。

class Solution {
    public int maxSubArray(int[] nums) {
        int res = Integer.MIN_VALUE;
        int count = 0;
        for(int i = 0; i < nums.length; i++){
            count += nums[i];
            if(count > res){
                res = count;
            }
            if(count < 0){
                count = 0;
            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值