数据结构与算法(六) 贪心算法

news2024/9/20 18:47:07

这篇文章来讲贪心算法(Greedy Algorithm),这是一种特殊的动态规划算法


1、本质

我们在之前的文章中讲过,动态规划可以解决一类具有最优子结构和重叠子问题特征的问题

贪心算法本质上是一种特殊的动态规划算法,因此在看这篇文章前,建议先看之前动态规划的文章


回到正题,先来简单回顾下动态规划,动态规划的核心就是状态转移方程

无论是自上而下利用递归的解题思路,还是自下而上利用递推的解题思路,都可以用状态转移方程串联起来

状态转移方程的关键就是怎么根据较小规模的问题推导出较大规模的问题

在每一步推导的过程中,其实就是在做一件事情,即在当前状态下,尝试所有选择,到达新的状态

然后不断重复这个过程,从已知状态开始直至待求状态结束


跳跃游戏 II | leetcode45

给定一个非负整数数组,初始位于数组的第一个下标,数组中的元素代表所在位置可以跳跃的最大长度

假设一定可以到达数组最后一个位置,问最少需要跳多少次才能到达

为了更好地理解这一过程,这里以跳跃游戏为例,假设给定数组 [2, 3, 1, 1, 4],画出递归树如下:

在这里插入图片描述

注意到这个问题是满足最优子结构和重叠子问题的,因此必定可以用动态规划来解

这里以自上而下的解题思路为例给出代码

class Solution {
public:
    vector<int> memo;

    // 动态规划
    int dp(vector<int>& nums, int pos) {
        int n = nums.size();
        // 边界条件
        if (pos >= n - 1) {
            return 0;
        }
        // 查备忘录
        if (memo[pos] != n) {
            return memo[pos];
        }
        // 尝试所有选择,优化点
        // 写备忘录
        for (int i = 1; i <= nums[pos]; i++) {
            memo[pos] = min( memo[pos], dp(nums, pos + i) + 1 );
        }
        // 返回
        return memo[pos];
    }

    int jump(vector<int>& nums) {
        int n = nums.size();
        memo = vector<int>(n, n); // 初始化备忘录
        return dp(nums, 0);
    }
};

动态规划在每一步的推导过程中,都要尝试所有选择,这是因为在某些问题中无法判断哪个选择是最优选择

但是贪心算法无需尝试所有选择,而是可以通过推导得出当前最优解,并且局部最优解可以到达全局最优解

假设现在站在索引 0 的位置上,其可跳长度为 2,那么可能的选择有:跳到索引 1 的位置上、跳到索引 2 的位置上。动态规划的思路是,我不知道这两种做法哪种好,所以我就两种都试一下。而贪心算法建立在更强的约束上,在面临多个选择时,能判断出当前最优解是什么。回到上述例子,索引 1 位置的可跳长度是 3,如果当前选择跳到索引 1,那么下一步最远可以跳到索引 4;同理,索引 2 位置的可跳长度是 1,如果当前选择跳到索引 2,那么下一步最远可以跳到索引 3。明显当前跳到索引 1 是优于跳到索引 2 的,因为下一步可跳位置的选择更多,因此贪心算法就会直接选择跳到索引 1,而不会再去尝试跳到索引 2

class Solution {
public:
    // 无需递归
    int jump(vector<int>& nums) {
        int n = nums.size();
        int pos = 0; // 当前位置
        int ans = 0; // 当前步数
        // 进主流程
        while (pos < n - 1) {
            int nxtPos = 0; // 下一位置
            int maxPos = 0; // 最远位置
            // 直接判断最优位置
            // 无需尝试所有选择
            for (int i = 0; i <= nums[pos]; i++) {
                if (pos + i >= n - 1) {
                    nxtPos = pos + i;
                    break;
                }
                if (maxPos < pos + i + nums[pos + i]) {
                    maxPos = pos + i + nums[pos + i];
                    nxtPos = pos + i;
                }
            }
            pos = nxtPos;
            ans = ans + 1;
        }
        // 返回
        return ans;
    }
};

2、核心

贪心算法是一种特殊的动态规划算法,它的约束更强,适用场景更少,但一般来说具有更高的效率

这是因为动态规划需要尝试所有选择,而贪心算法则可以通过推导直接选择当前最优解


贪心算法所解决的问题除了要满足最优子结构外,更重要的是要满足贪心选择性质

所谓的贪心选择性质就是说每次选择仅依赖于之前的选择,而不依赖于后续的选择

理解这个性质很关键,这是贪心算法的核心

在每一步推导过程中,贪心算法在当前状态下做局部最优解以到达下一状态,直至到达待求的问题

对于一个具体的问题,使用贪心算法前必须要证明每一步推导所做的局部最优解能到达全局最优解


凑零钱问题 | leetcode322

给定一个整数数组 coins 表示不同面额的硬币,以及一个整数 amount 表示总金额

假设每种硬币的数量是无限的,问凑成总金额所需的最少的硬币个数,若无法凑成,则返回 -1

注意,如果盲目使用贪心算法,可能取不到全局最优解,这里以凑零钱问题为例说明这种情况

对于上述问题,使用贪心算法的一个直观思路是:每次选择面额最大的硬币,直至凑满总金额

这对于普通的面额分布确实是可行的,例如 coins = [1, 5, 10], amount = 13

但对于特殊的面额分布就会出现问题,例如 coins = [1, 4, 5 ], amount = 13

如果使用贪心算法,那么选择的硬币依次是 5、5、1、1、1,但最优选择方式却是 5、4、4


3、框架

贪心算法的框架很简单,总结起来就是一句话:在推导过程中,每次都选择当前最优解

只不过在使用贪心算法时需要特别注意,局部最优解必须能到达全局最优解


4、例题

(1)无重叠区间 | leetcode435

给定一个区间集合,返回移除区间的最小数量,使得剩余的区间互不重叠

基本思路

  1. 题目要求移除最少数量的区间使得剩余的区间互不重叠

    可转换为选择最多数量的区间使得选择的区间互不重叠

  2. 根据贪心算法的思路,每次选择右端点最小且不与先前区间重叠的区间即可

    这是因为同样是增加一个区间,如果选择右端点较小的区间,那么以后选择的可能性更多

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        // 特判
        int n = intervals.size();
        if (n <= 0) {
            return 0;
        }
        // 预处理,为了提高效率
        // 按右端点从小到大排序
        sort(intervals.begin(), intervals.end(), [](const auto& u, const auto& v) {
            return u[1] < v[1];
        });
        // 每次选择右端点最小且不与先前区间重叠的区间
        int ans = 1;
        int end = intervals[0][1];
        for (int i = 1; i < n; i++) {
            // 因为数组已提前排序,所以按顺序遍历就能选择右端点最小的区间
            // 然后只需保证当前区间与先前区间无重叠,也即当前区间的左端点大于等于先前区间的右端点
            if (intervals[i][0] >= end) {
                ans = ans + 1;
                end = intervals[i][1];
            }
        }
        // 返回结果
        return n - ans;
    }
};

(2)单调递增的数字 | leetcode738

给定一个整数,返回小于等于这个数字的最大数字,使得返回的数字是单调递增的

基本思路

  1. 从折线图的角度来看,从左往后找第一个谷顶,将这个数字减去一,将后面数字改为九

    如:13542 -> 13499

  2. 若第一个谷顶是平的,则找谷顶最左边的位置,将这个数字减去一,将后面数字改为九

    如:15542 -> 14999

class Solution {
public:
    int monotoneIncreasingDigits(int n) {
        if (n < 10) {
            return n;
        }
        string s = std::to_string(n);
        int m = s.size();
        int i = 0;
        while (i < m - 1 && s[i] <= s[i + 1]) {
            i++;
        }
        if (i == m - 1) {
            return n;
        }
        while (i > 0 && s[i] == s[i - 1]) {
            i--;
        }
        s[i] = s[i] - 1;
        i++;
        while (i < m) {
            s[i] = '9';
            i++;
        }
        return stoi(s);
    }
};

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

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

相关文章

微前端:qiankun的五种通信方式

背景 今天盘点一下 qiankun 父子应用的通信方式都有哪些&#xff0c;我发现了 5 种。 1、localStorage/sessionStorage 2、通过路由参数共享 3、官方提供的 props 4、官方提供的 actions 5、使用vuex或redux管理状态&#xff0c;通过shared分享 接下来我们一个一个进行说明 …

S/4HANA(本地部署或云版)跟 SAP 家族系统以及非SAP系统的集成,到底什么是推荐的方式?

笔者的知识星球&#xff0c;有朋友提问&#xff1a; S4HANA(本地部署或云版)跟SAP家族系统以及非SAP系统的集成&#xff0c;sap的标准/推荐做法是通过BTP还是直接连接&#xff0c;或者是根据目标系统分别选择&#xff1f;有参考链接最好了。 首先 SAP S/4HANA 的技术栈&#xf…

java数组应用(栈和队列以及酒店模拟)

1.编写程序,使用一维数组,模拟栈数据结构 要求1.栈内可以存放任意数据2.栈内提供push方法模拟压栈3.栈内提供pop方法模拟出栈4.栈的大小默认为10 新建MyStack类 /*** author DeYou* date 2022/11/10 23:01*/ public class MyStack {Object[] element;int index;public MyStack…

blender Eevee渲染器

文章目录简介.采样.环境光遮蔽.辉光.景深.次表面散射.屏幕空间反射.间接光照明.阴影.体积.简介. 1 EV渲染器是实时渲染&#xff0c;类似于游戏引擎,效果差于Cycles 2 EV渲染器使用的设备是显卡 采样. 1 渲染和视图分别指渲染模式和视图模式的采样值&#xff0c;值越大&#…

Android Studio 中MotinLayout的简单使用

​ 一、什么是MotionLayout MotionLayout 是一种布局类型&#xff0c;可帮助您管理应用中的运动和微件动画。MotionLayout 是 ConstraintLayout 的子类&#xff0c;在其丰富的布局功能基础之上构建而成。 二、使用MotionLayout的准备 1.添加MotionLayout依赖项 要在项目中使用…

【菜鸡读论文】Face2Exp: Combating Data Biases for Facial Expression Recognition

【菜鸡读论文】Face2Exp: Combating Data Biases for Facial Expression Recognition 最近上海开始降温了&#xff0c;而且常常下雨&#xff0c;天气开始变得好冷啊&#xff01;以前年轻的时候冬天经常穿的少&#xff0c;现在膝盖开始有点遭不住了&#xff0c;小伙伴们一定要保…

Go字符串拼接6种方式及其性能测试:strings.builder最快

Go字符串拼接常见的方式有加号、fmt.Sprintf、strings.Builder、bytes.Buffer、strings.join、切片。 package concat_stringimport ("bytes""fmt""strconv""strings""testing" )const numbers 100// func BenchmarkStri…

Kafka安装与配置

1、Java环境为前提 &#xff08;1&#xff09;上传jdk-8u261-linux-x64.rpm到服务器并安装&#xff1a; rpm -ivh jdk-8u261-linux-x64.rpm &#xff08;2&#xff09;配置环境变量&#xff1a; vim /etc/profile # 生效 source /etc/profile # 验证 java -version 2、Zook…

贝叶斯定理~~概率分布

还是搬来了基础自己学习用哦 ~~ 从最基础的概率论到各种概率分布全面梳理了基本的概率知识与概念&#xff0c;这些概念可能会帮助我们了解机器学习或开拓视野。这些概念是数据科学的核心&#xff0c;并经常出现在各种各样的话题上。重温基础知识总是有益的&#xff0c;这样我们…

适应性哈夫曼编码(Adaptive Huffman coding)

适应性哈夫曼编码适应性哈夫曼编码简介算法示例适应性哈夫曼编码 简介 适应性哈夫曼编码&#xff08;Adaptive Huffman coding&#xff09;&#xff0c;又称动态哈夫曼编码&#xff08;Dynamic Huffman coding&#xff09;&#xff0c;是基于哈夫曼编码的适自适应编码技术。它…

深度学习Day07

Recurrent Neural Network 智慧客服——Slot Filling 根据上 下文产生不同的output

Java搭建宝塔部署实战毕设项目WMS仓库管理系统源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的毕业设计项目WMS仓库管理系统源码。 技术架构 技术框架&#xff1a;SpringMVC Mybatis Ehcache bootstrap jquery mysql5.7运行环境&#xff1a;jdk8 nginx1.20 tomcat9 In…

基于PSO粒子群算法的MPPT最大功率跟踪Simulink仿真,PSO采用S函数实现

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 MPPT控制器的全称是“最大功率点跟踪”&#xff08;Maximum Power Point Tracking&#xff09;太阳能控制器&#xff0c;是传统太阳能充放电控制器的升级换代产品。MPPT控制器能够实时侦测太阳能…

30岁自学Python转行靠谱吗?

前言 30岁自学Python转行靠谱吗&#xff1f;若啃学习任何时候都不晚&#xff0c;关键是学习完用来做什么。提高工作效率&#xff0c;写些脚本实现自动化办公这些完全没问题。如果学python是为了转开发&#xff0c;建议慎重考虑&#xff0c;程序开发转Python相对容易些&#xf…

[附源码]计算机毕业设计血库管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

计算机网络——运输层【重点】

运输层概述 概念 进程之间的通信 从通信和信息处理的角度看&#xff0c;运输层向它上面的应用层提供通信服务&#xff0c;它属于面向通信部分的最高层&#xff0c;同时也是用户功能中的最低层。当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时&…

【SpingBoot拦截器】实现两个接口,配置拦截路径

文章目录SpingBoot拦截器拦截器与过滤器的区别&#xff1a;1.HandlerInterceptor接口2.WebMvcConfigurer接口3.示例&#xff1a;SpingBoot拦截器 【SpringWeb框架中的拦截器作用类似于过滤器&#xff0c;都可以对一个请求进行拦截处理。】 我们可以用拦截器做很多事情&#xf…

使用HTML制作静态网站作业——我的校园运动会(HTML+CSS)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Java项目:SSM企业门户网站

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目分为前后台&#xff0c;前台主要用于官网展示&#xff0c;后台主要为管理员管理&#xff0c; 管理员角色包含以下功能&#xff1a; 管理员…

Rook Ceph浅谈

storageclass是最省心的&#xff0c;上面三种删除了容器存储还在 下图是管理员定义pv&#xff0c;用户不知道这些细节 PersistentVolume pv会跟后端的存储rbd对接 用户通过PVC调用PV ,根据10G容量去pv里找相匹配的自动关联 上图就叫 pvc-demo StorageClass 更厉害 管理员…