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

news2024/9/22 4:29:15

文章目录

    • 理论基础
    • 解题步骤
    • 455.分发饼干
      • 思路
      • 小结
    • 376.摆动序列
      • 简单思路
      • 贪心思路
    • 53.最大子序和
      • 思路

今天是贪心算法的第一天

理论基础

贪心的本质是选择每一阶段的局部最优,从而达到全局最优

在理论上,能使用贪心解决的问题有两个特点:具有最优子结构 和 贪心选择性,其中贪心选择性是贪心算法能使用的关键,只有最优子结构,可能用dp,可能用贪心,无法说明贪心适用。

  • 贪心选择性:每一步贪心选出来的一定是原问题的最优解的一部分。

  • 最优子结构:每一步贪心选完后会留下子问题,子问题的最优解和贪心选出来的解可以凑成原问题的最优解。

贪心的过程是只考虑当前的最优情况,然后遗留下一个规模变小了,但性质相同的子问题,如果贪心选出的是最优解,则本次贪心选出来的解一定是最终的最优解的一部分;剩下的子问题中的最优解与刚贪心选出来的解可以凑成原问题的最优解

实际上,贪心没有某类具体适用的题目,它是一种思想:局部最优之和为整体最优。在做题或面试时,手动模拟一下感觉可以局部最优推出整体最优,而且想不到反例,那么就试一试贪心。贪心有的时候就是常识性的推导,像1+1=2一样自然;有时需要数学推导,例如这道题目:链表:环找到了,那入口呢? (opens new window),这道题不用数学推导一下,就找不出环的起始位置,想试一下就不知道怎么试,这种题目确实需要数学简单推导一下

解题步骤

贪心算法一般分为如下四步:

  • 将问题分解为若干个子问题
  • 找出适合的贪心策略
  • 求解每一个子问题的最优解
  • 将局部最优解堆叠成全局最优解

这个四步其实过于理论化了,我们平时在做贪心类的题目 很难去按照这四步去思考,真是有点“鸡肋”

做题的时候,只要想清楚 局部最优 是什么,如果推导出全局最优,其实就够了

贪心这一部分,实践比理论重要,直接上题目

455.分发饼干

题目链接:455. 分发饼干 - 力扣(LeetCode)

思路

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

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

举个例子:

img

饼干9只有喂给胃口为7的小孩,这样才是整体最优解

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

代码实现

class Solution
{
    public:
    int findContentChildren(vector<int>& g, vector<int>& s)
    {
        sort(s.begin(), s.end());
        sort(g,begin(), g.end());
        int index = s.size()-1;	// 饼干数组的下标
        int result = 0;
        // 从后向前遍历数组g
        for(int i=g.size()-1; i>=0; --i)
        {
            if(index >= 0 && s[index] >= g[i])
            {
                result++;
                index--;
            }
        }
        return result;
    }
};
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)

注意到代码中使用index来控制饼干数组的遍历,而不是使用for循环,因为一个饼干不能分配给多个小孩,每次外层遍历结束时,都要记录上次饼干数组遍历到的位置

另一个思路:小饼干喂饱小胃口,即先满足小胃口

代码实现

class Solution {
public:
    int findContentChildren(vector<int>& g, vector<int>& s) {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int index = 0;
        for(int i = 0; i < s.size(); i++) { // 饼干
            if(index < g.size() && g[index] <= s[i]){ // 胃口
                index++;
            }
        }
        return index;
    }
};

小结

这是贪心的一道入门题目。想清楚局部最优,想清楚全局最优,感觉局部最优是可以推出全局最优,并想不出反例,那么就试一试贪心

376.摆动序列

题目链接:376. 摆动序列 - 力扣(LeetCode)

简单思路

核心思想:最长摆动序列中相邻的两个数字对应着一次正负转换。原序列中连续的上升,对应着最长摆动序列中的一次上升,下降同理。

本题求最长摆动序列的长度,只需要求有多少次正负转换,正负转换的次数+1 就是 最长摆动序列的长度。以示例二为例,如图:

376.摆动序列

这个序列的最长摆动序列为[1, 17, 5, 15, 5, 16, 8],长度为7,每两个数字之间都对应着一次正负转换,对于连续的上升,如5->10->13->15,我们将其看做一次上升,这对应着最长摆动序列中5->15的上升

代码实现

class Solution {
public:
    int wiggleMaxLength(vector<int>& nums) {
       int count = 1;
       int gap = 0;
       for(int i=1; i<nums.size(); ++i)
       {
            int cur = nums[i] - nums[i-1];
            if(cur != 0 && cur*gap <= 0)    // 如果异号,说明已经出现了正负翻转
            {
                gap = cur;
                count++;
            }
       }
       return count;
    }
};

贪心思路

核心思想:要使摆动序列尽量长,需要使摆动序列中相邻的两个数字之间相差最大

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

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

以示例二为例,如图:

376.摆动序列

最终,局部峰值和峰谷组成的序列即为最长摆动序列

相对于简单思路,这个思路可以获得最长摆动序列,不仅仅是它的长度

整体思路:我们用preDiff记录 上一个峰值或峰谷 的差值nums[prev + 1] - nums[prev],curDiff记录当前差值nums[cur + 1] - nums[cur],就像在函数中寻找极大值和极小值一样,如果preDiff < 0 && curDiff > 0,则当前的值nums[cur]就是极大值;如果preDiff > 0 && curDiff <= 0,则当前的值nums[cur]就是极小值,这些峰值和峰谷组合在一起,即可构成最长摆动序列

本题需要考虑异常情况:考虑平坡。平坡分成三种:上下中间有平坡,单调有平坡,数组首尾两端有平坡,如图所示:

上下中间有平坡

img

单调有平坡

img

数组首尾两端有平坡

376.摆动序列1

  • 修正判断峰值或峰谷的条件:(preDiff <= 0 && curDiff > 0) || (preDiff >= 0 && curDiff < 0)
  • preDiff更新时机:我们只需要在 这个坡度 摆动变化的时候,更新 prediff 就行,这样 preDiff 在 单调区间有平坡的时候 就不会发生变化,造成我们的误判

代码实现

class Solution
{
    public:
        int wiggleMaxLength(vector<int>& nums)
        {
            if(nums.size() <= 1)
            {
                return nums.size();
            }
            int curDiff = 0;    // 当前一对差值
            int preDiff = 0;    // 前一对差值
            int result = 1;     // 记录峰值个数,默认有一个峰值
            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;  // 只有在波动时才会更新preDiff
                }
            }
            return result;
        }
};

53.最大子序和

题目链接:53. 最大子数组和 - 力扣(LeetCode)

思路

可以首先考虑一个较简单的序列,如[-2, 1],这个序列的最大子序和为1,是从1开始计算的。负数只会拉低总和,因此当“连续和“为负数时应该放弃,而从下一个元素重新开始计算

整体思路:遍历nums数组,sum记录当前的连续和,result记录之前最大的连续和。如果sum>=0,则继续计算连续和,sum += nums[i];如果sum<0,则需要重新开始计算连续和,sum = nums[i]。如图所示:

53.最大子序和

红色表示连续和

代码实现

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int result = nums[0];
        int sum = 0;

        for(int num : nums)
        {
            if(sum >= 0)
            {
                sum += num;
            }else
            {
                sum = num;
            }
            result = max(result, sum);
        }
        return result;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

注意:只要连续和还是正数,它还对后面的元素起到增大总和的作用,就要继续计算连续和,并不是遇到负数就选择起始位置。result在遍历过程中时刻记录最大的连续和,不用担心sum加上负数后连续和减小 而对最后结果有影响

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2069893.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

buuctf [HDCTF2019]Maze

前言&#xff1a;做题笔记。 常规 下载 解压 查壳 脱壳后用32IDA Pro打开。 得&#xff0c;迷宫类型的题目。(字符串有说。) 咳&#xff0c;此前思路对半分不行了。。。 合理猜测步数为&#xff1a;14。 那可以看看7 * 10的迷宫类型。(手动猜测的时候去取倍数如&#xff1a;0 2…

什么牌子的蓝牙耳机性价比高?2024年四款最值得买王牌耳机推荐!

在当前的手机备件市场中&#xff0c;蓝牙耳机已经逐渐成为智能手机备件的热门之选。然而&#xff0c;面对众多的耳机品牌和型号&#xff0c;消费者在选购时可能会感到困惑&#xff0c;稍微不留言就会买到不专业产品&#xff0c;那么什么牌子的蓝牙耳机性价比高&#xff1f;作为…

STM32的串口通信——HAL库

TTL串口 TTL串口仅仅需要两根数据线就可以进行串口通信&#xff1a; ①一条是从A设备发送的IO口连接到B设备的接收IO口 ②一条是从B设备发送的IO口连接到A设备的接收IO口 ③共地&#xff08;GND&#xff09;是两个设备通信的前提&#xff08;保证他们的电平标准一致&#x…

使用css如何获取最后一行的元素?使用css解决双边框问题

一、项目场景&#xff1a; 在小程序上需要实现一个如下图的ui效果图 需要满足以下条件 一行放不下 自动换行最后一行或者只有一行时&#xff0c;文字底部不能有线 二、初版实现 按照上面的要求&#xff0c;最开是的实现代码如下 我是给每一个元素都添加了一个下边框&#x…

Python酷库之旅-第三方库Pandas(095)

目录 一、用法精讲 406、pandas.DataFrame.index属性 406-1、语法 406-2、参数 406-3、功能 406-4、返回值 406-5、说明 406-6、用法 406-6-1、数据准备 406-6-2、代码示例 406-6-3、结果输出 407、pandas.DataFrame.columns属性 407-1、语法 407-2、参数 407-3…

楼顶气膜羽毛球馆:城市健身新空间—轻空间

随着城市化进程的加快&#xff0c;城市土地资源愈发紧张&#xff0c;如何高效利用有限的空间成为一大挑战。楼顶气膜羽毛球馆作为一种创新的体育场馆建设方式&#xff0c;凭借其独特的优势&#xff0c;逐渐成为城市健身的新宠。它不仅有效利用了楼顶闲置空间&#xff0c;还为市…

新160个crackme - 039-eKH.1

运行分析 需要破解Name和Serial&#xff0c;写出注册机 PE分析 - Delphi程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索关键字符串&#xff0c;跳转到关键代码 静态分析&#xff0c;修改变量如上&#xff0c;关键在于sub_427A20函数返回值需要大于等于1…

“双指针”算法下篇

WeChat_20240806081335 对双指针这一思想在OJ 里面的相关应用&#xff0c;感兴趣的友友们&#xff0c;可以看下此篇博客 https://blog.csdn.net/X_do_myself/article/details/141291451?spm1001.2014.3001.5502 目录 一盛最多水的容器 1题目链接&#xff1a;盛最多水的容器…

EmguCV学习笔记 VB.Net 6.5 凸包和凸缺陷

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

sgsegse

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

货车制造5G智能工厂工业物联数字孪生平台,推进制造业数字化转型

货车制造5G智能工厂工业物联数字孪生平台&#xff0c;推进制造业数字化转型。随着5G技术的飞速发展与工业物联网的深度融合&#xff0c;货车制造5G智能工厂工业物联数字孪生平台应运而生&#xff0c;它不仅重新定义了生产模式&#xff0c;更以强大的技术驱动力&#xff0c;推动…

开放世界目标检测:检测区分出未知物体

开放世界目标检测&#xff1a;检测区分出未知物体 01 Abstract 开放世界目标检测旨在识别未见过类别的目标&#xff0c;并在提供注释后逐步识别这些目标。与传统的只限于预定义类别的范式不同&#xff0c;这种设置承诺通过使用与类别无关的信息来持续且通用地估计目标性。然而…

Java码农35岁之后只能送外卖?

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/2208.html Hey&#xff0c;Java界的小伙伴们&#xff0c;有没有感受到互联网行业这一场没有硝烟的“代际战争”&#xff1f;&#x1f916;&#x1f4a5; 关于“35岁后只…

配电房挂轨机器人巡检系统的主要优点包括

背景 配电房是724h工作的封闭环境&#xff0c;人工巡检无法在时间上和空间上对配电室进行全量监控。有限的巡检时间&#xff0c;必然带来设备运转的黑盒时间&#xff0c;设备故障和隐患无法及时监控与消缺。因而不可避免存在漏检、误检的情况&#xff0c;不仅容易隐藏电力系统…

AI Agent产品经理血泪史:一年来我摸过的那些石头【Tools篇】

前几天刚好看到一篇关于GPT-6的报道&#xff0c;才想起来还有这麽回事情&#xff0c;于是赶紧把草稿捞出来改改交个任务。 至于为什麽贴这张图&#xff0c;以及为什麽血泪史从Tools开篇。 那是因为你看&#xff0c;即使到了GPT-6的时代&#xff0c;Tools仍然是AI Agent落地的…

ElementPlus下拉框输入框对齐问题

1.问题 2.解决方法 2.1label-width 说明&#xff1a;el-form中label-width设置为auto 2.2 label-wdith固定值 说明&#xff1a;如果在el-form-item里面设置了label-width"100px"&#xff1b;采用宫格布局。 .demo-one{display: grid;grid-template-columns: repe…

C++:vector篇

前言&#xff1a; 本篇仅介绍vector中常用的函数接口&#xff0c;如果需要详细的请到官网查看。 vector是一种动态数组&#xff0c;能够自动调整大小。与数组类似&#xff0c;vector使用连续内存来存储元素&#xff0c;允许高效访问&#xff0c;但可以动态增加容量。为了应对容…

买了服务器后如何正确挂载数据盘|什么是系统盘,什么是数据盘

一、前言 我们买了服务器后&#xff0c;一般会再买一个数据盘&#xff0c;如果没有数据盘&#xff0c;万一服务器系统出现问题后数据丢失就完了&#xff0c;什么数据都没了&#xff0c;所以为了避免意外的发生&#xff0c;我们通常会再买一个数据盘 如上图&#xff0c;我就在…

差一点通关某公司面试靶场

没有web4和web8&#xff0c;web2的4没做出来。 web1 国光ssrf改版靶场 1 直接读取flag 3 使用file协议读取hosts IP为172.18.240.5 dict探针内网主机开启常见端口 172.18.240.1:80 172.18.240.5:3306 172.18.240.1:3306 172.18.240.5:80 172.18.240.5:8080 172.18.240.1:…

RocketMQ Dashboard

rocketmq-dashboard是一个可视化查看和管理RocketMQ消息队列的工具 官方地址&#xff1a;RocketMQ Dashboard | RocketMQ 1、点击下载源码 2、下载并解压&#xff0c;切换至源码目录rocketmq-dashboard-1.0.0 3、修改配置文件 4、编译 rocketmq-dashboard打成jar包 &#xf…