day31 贪心算法 分发饼干 摆动序列 最大子序列和

题目1:455  分发饼干

题目链接:分发饼干
对题目的理解

每个孩子最多只能有1块饼干,g[i]是每个孩子满足胃口的最小饼干尺寸,每个饼干的尺寸是s[j],s[j]>=g[i],满足尽可能多的孩子,至少有一个孩子,至少有0块饼干。

贪心算法

法一:大饼干优先喂大孩子

为了满足更多的小孩,大尺寸的饼干既可以满足胃口大的孩子也可以满足胃口小的孩子,那么就应该优先满足胃口大的,使用大饼干满足胃口大的孩子

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

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

代码

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
    sort(g.begin(),g.end());
    sort(s.begin(),s.end());
    int index = s.size()-1;
    int sum = 0;
    for(int i=g.size()-1;i>=0;i--){
       if(index>=0 && s[index]>=g[i]){
           sum++;
           index--;
       }
    }
    return sum;
    }
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

用了一个 index 来控制饼干数组的遍历,遍历饼干并没有再起一个 for 循环,而是采用自减的方式,这也是常用的技巧

!!!注意不可以先遍历饼干再遍历胃口,因为有可能胃口很大,超过了饼干的尺寸,那么内层因为不满足if条件,胃口一直停留在最大的那里,而饼干尺寸一直向前移,这样得出的结果是错误的

反例:饼干一直动,胃口不动

法二:小饼干先喂小胃口

遍历顺序是先遍历的饼干,再遍历的胃口

如果先遍历胃口,再遍历饼干,那么可能出现,饼干一直不满足条件,一直比胃口小,那么会出现胃口一直动(会越来越大),饼干不动的现象

反例  胃口一直动,饼干不动

代码

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
    sort(g.begin(),g.end());
    sort(s.begin(),s.end());
    int index = 0;
    int sum = 0;
    for(int i=0;i<s.size();i++){
       if(index<g.size() && s[i]>=g[index]){
           sum++;
           index++;
        }
    }
    return sum;
    }
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

题目2:376 摆动序列

题目链接:摆动序列

对题目的理解

连续数字之间的差在正数和负数之间交替出现,称为摆动序列;仅含一个元素或两个不等元素的序列也是摆动序列;子序列可以从原始序列中删除(或不删除)元素获得,元素的顺序不变

返回整数数组中摆动序列中的最长子序列的长度,数组中至少包含一个元素。

贪心算法

局部最优:删除单调坡度上的节点(不包括单调坡度两端的节点),那么这个坡度就可以有两个局部峰值

整体最优:整个序列有最多的局部峰值,从而达到最长摆动序列

没有必要真的删除数组中的元素,遇到摆动时,做一个加加的操作即可,因为题目要求的是最长摆动子序列的长度,所以只需要统计数组的峰值数量就可以了(相当于是删除单一坡度上的节点,然后统计长度)

这就是贪心所贪的地方,让峰值尽可能的保持峰值,然后删除单一坡度上的节点。

如何判断摆动条件

本题要考虑三种情况:

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

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

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

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

伪代码

代码

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
        int result = 1;
        int prediff = 0;
        int curdiff = 0;
        for(int i=0;i<nums.size()-1;i++){
            curdiff = nums[i+1]-nums[i];
            if((prediff>=0 && curdiff<0)||(prediff<=0 && curdiff>0)){
                result++;
                prediff = curdiff;
            }
        }
        return result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

动态规划

本题也可以使用动态规划的方法

题目3:53 最大子序列和

题目链接:最大子序列和

对题目的理解

找出整数数组中具有最大和的连续子数组(至少包含一个元素),返回最大值。

!!!注意题目说的是连续和,注意连续,注意连续,注意连续!!!

贪心算法

连续和为负数,直接从下一个数字计算,若负数加上下一个数则定会让下一个数变小,所以干脆就不加前面的连续和了,从当前位置的数重新算起。

局部最优:当前“连续和”为负数的时候立刻放弃,从下一个元素重新计算“连续和”,因为负数加上下一个元素 “连续和”只会越来越小;只要连续和还是正数就会 对后面的元素 起到增大总和的作用。 所以只要连续和为正数我们就保留。

全局最优:选取最大“连续和”

局部最优的情况下,并记录最大的“连续和”,可以推出全局最优

图中黑色箭头表示重新开始遍历

伪代码

代码

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int count=0;//记录连续和
        int result = INT_MIN;//记录最大连续和
        for(int i=0;i<nums.size();i++){
            count += nums[i];
            if(count>result) result = count;
            if(count<0) count = 0;//连续和为负数,重新计算连续和
        }
        return result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

动态规划

本题也可以使用动态规划求解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值