LeetCode从入门到超凡(四)深入浅出理解贪心算法

news2024/11/17 13:54:39

head-bar

引言

大家好,我是GISer Liu😁,一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的LeetCode学习总结文档;本文主要讲解贪心算法。💕💕😊


介绍

贪心算法是一种经典的算法策略,广泛应用于解决最优化问题。在许多情况下,贪心算法能够以简单且高效的方式解决复杂问题。因此,掌握贪心算法对开发人员和算法爱好者来说非常重要。本文旨在帮助读者深入理解贪心算法的工作原理、特征、正确性证明及其实际应用,并通过多个实例的分析和Python代码实现来巩固理解。

目标读者

本文适合有一定编程基础,尤其是具备基础算法和数据结构知识的读者,旨在帮助这些读者深入理解贪心算法并掌握其应用。


一、贪心算法基础

1. 什么是贪心算法?

贪心算法是一种在每一步选择中都采取当前状态下最优或最有利的选择,从而期望通过局部最优解最终得到全局最优解的算法。

  • 定义:贪心算法是一种逐步构建解的算法,每一步都选择当前看起来最好的局部解决方案,并希望这些局部解决方案的集合能够产生全局最优解。
  • 核心思想:局部最优 -> 全局最优。

2. 贪心算法的特征

uml

贪心算法在使用时需要满足两个特征:

  1. 贪心选择性质:即从局部最优的角度出发,当前的最优选择不依赖于未来的选择。每一步的选择是局部最优解。
  2. 最优子结构:即问题的最优解由其子问题的最优解组成。这意味着问题可以通过递归的方式求解,且子问题的最优解与整个问题的最优解是一致的。

3. 贪心算法的优缺点

  • 优点:简单、高效,通常时间复杂度较低。由于不需要像动态规划那样保存子问题的解,贪心算法的空间复杂度也较低。
  • 缺点:贪心算法不能保证在所有问题上都能找到全局最优解,它只适用于能够满足贪心选择性质和最优子结构性质的问题。选择错误的策略,可能导致非最优解。

二、贪心算法的工作原理

1. 贪心算法的三步走

贪心算法可以通过以下三步来求解问题:

  1. 转换问题:将复杂的问题转换为子问题,使得每一步都有一个明确的最优选择。
  2. 贪心选择性质:在每一步中选择当前最优解,从而构建局部最优解。

  1. 最优子结构性质:将每一步的局部最优解结合起来,最终得到全局最优解。

如果不能利用子问题的最优解推导出整个问题的最优解,那么这种问题就不具有最优子结构。😎

2. 贪心算法的正确性证明

要证明贪心算法能够求解问题的最优解,常用以下两种方法:

  1. 数学归纳法:先计算出边界情况(例如 n=1)的最优解,然后再证明对于每个 n, F n + 1 F_{n + 1} Fn+1 都可以由 F n F_n Fn 推导出。
  2. 交换论证法:从最优解出发,在保证全局最优不变的前提下,如果交换方案中任意两个元素 / 相邻的两个元素后,答案不会变得更好,则可以推定目前的解是最优解。

三、贪心算法的应用实例

接下来作者将通过几个经典问题来演示贪心算法的实际应用,并通过Python代码来解释每个问题的贪心策略。

1.分发饼干问题

问题描述:假设你有若干孩子和若干饼干,每个孩子有一个胃口值,每个饼干有一个尺寸。当且仅当某个饼干的尺寸大于等于某个孩子的胃口值时,该孩子才会得到饼干。你的目标是尽可能满足更多的孩子。

贪心策略优先满足胃口最小的孩子,因为最小的胃口更容易得到满足

代码实现

def find_content_children(g, s):
    """
    分发饼干问题
    :param g: 孩子的胃口数组
    :param s: 饼干的尺寸数组
    :return: 能满足的最大孩子数量
    """
    # 对孩子的胃口和饼干尺寸进行排序
    g.sort()
    s.sort()

    child_i = 0
    cookie_i = 0

    # 遍历饼干和孩子,尽量满足更多的孩子
    while child_i < len(g) and cookie_i < len(s):
        if s[cookie_i] >= g[child_i]:
            # 如果当前饼干能满足当前孩子,分发饼干
            child_i += 1
        cookie_i += 1  # 无论是否满足孩子,尝试下一个饼干
    
    return child_i

# 示例
g = [1, 2, 3]
s = [1, 1]
print(find_content_children(g, s))  # 输出: 1
过程分析:
  1. 首先将孩子的胃口数组 g 和饼干尺寸数组 s 排序。
  2. 通过遍历饼干和孩子,尽量让每个饼干满足胃口最小的孩子。
  3. 使用贪心策略,尽可能满足最多的孩子。

2. 无重叠区间问题

问题描述:给定一组区间,找到最少移除的区间数量,使得剩余区间互不重叠。

贪心策略:按结束时间排序,优先选择结束时间最早的区间。

代码实现

def erase_overlap_intervals(intervals):
    """
    无重叠区间问题
    :param intervals: 区间数组
    :return: 需要移除的最小区间数量
    """
    if not intervals:
        return 0

    # 按区间的结束时间进行排序
    intervals.sort(key=lambda x: x[1])

    end = intervals[0][1]
    count = 0

    # 遍历所有区间
    for i in range(1, len(intervals)):
        if intervals[i][0] < end:
            # 如果当前区间与前一个区间重叠,则移除
            count += 1
        else:
            # 否则,更新结束时间
            end = intervals[i][1]
    
    return count

# 示例
intervals = [[1, 2], [2, 3], [3, 4], [1, 3]]
print(erase_overlap_intervals(intervals))  # 输出: 1
过程分析:
  1. 将区间按照结束时间排序,结束时间越早,后续可选择的空间越大。
  2. 遍历区间,检查当前区间是否与前一个区间重叠,如果重叠则移除当前区间。

3. 柠檬水找零问题

问题描述:你正在卖柠檬水,每位顾客给你 5、10 或 20 美元,你需要找零。假设一开始你没有任何零钱,问你能否在所有顾客完成交易后成功找零。

贪心策略:优先使用大面额的钞票找零。

代码实现

def lemonade_change(bills):
    """
    柠檬水找零问题
    :param bills: 顾客支付的钞票数组
    :return: 是否能够找零成功
    """
    five = ten = 0

    for bill in bills:
        if bill == 5:
            five += 1
        elif bill == 10:
            if five == 0:
                return False
            five -= 1
            ten += 1
        else:
            if ten > 0 and five > 0:
                ten -= 1
                five -= 1
            elif five >= 3:
                five -= 3
            else:
                return False
    
    return True

# 示例
bills = [5, 5, 5, 10, 20]
print(lemonade_change(bills))  # 输出: True
过程分析:
  1. 当顾客支付 10 美元时,优先找 5 美元。
  2. 当顾客支付 20 美元时,优先找 10 美元和 5 美元,如果没有 10 美元,则找 3 张 5 美元。
  3. 如果无法找零,返回 False

4. 分发糖果问题

问题描述:给定 N 个孩子,每个孩子有一个评分。你需要按照评分的高低给每个孩子发放糖果,保证评分高的孩子获得更多糖果,要求使用最少的糖果。

贪心策略

两次遍历,分别从左到右和从右到左,确保每个孩子都得到符合规则的糖果。

代码实现

def candy(ratings):
    """
    分发糖果问题
    :param ratings: 孩子的评分数组
    :return: 最少的糖果数量
    """
    n = len(ratings)
    candies = [1] * n

    # 从左到右遍历
    for i in range(1, n):
        if ratings[i] > ratings[i - 1]:
            candies[i] = candies[i - 1] + 1

    # 从右到左遍历
    for i in range(n - 2, -1, -1):
        if ratings[i] > ratings[i + 1]:
            candies[i] = max(candies[i], candies[i + 1] + 1)

    return sum(candies)

# 示例
ratings = [1, 0, 2]
print(candy(ratings))  # 输出: 5
过程分析:
  1. 从左到右遍历,确保评分高的孩子获得比左边多的糖果。
  2. 从右到左遍历,确保评分高的孩子获得比右边多的糖果。
  3. 两次遍历后,每个孩子的糖果数量即为最终结果。

四、贪心算法的局限性与扩展

1. 局限性

贪心算法并不总能保证全局最优解。对于某些问题,贪心策略可能导致次优解。下面是一个经典的反例:

  • 反例:最小化硬币找零问题
    在找零问题中,如果贪心地选择面额最大的钱币,可能无法找到最优解。例如,当找零为 14 时,面额为 10、7 和 1 的硬币,贪心算法会选择 10 和 4 个 1 元硬币,而最优解应为两个 7 元硬币。

2. 扩展

  • 动态规划与贪心算法的对比:动态规划通过保存子问题的最优解,能够处理更多复杂的最优化问题,适用于贪心算法不适合的场景。
  • 启发式算法:在某些复杂问题中,启发式算法可以作为贪心算法的扩展,用于近似求解复杂问题。

五、总结

总而言之,贪心算法是一种简单、高效的算法,适用于某些具有贪心选择性质和最优子结构性质的问题。通过几个经典问题的学习和实践,读者可以掌握贪心算法的应用。读者也可以深入学习动态规划、启发式算法等更高级的算法,进一步提升算法能力。😎👌


相关链接

  • 项目地址:LeetCode-CookBook
  • 相关文档:专栏地址
  • 作者主页:GISer Liu-CSDN博客

thank_watch

如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star🌟也可以😂.

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

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

相关文章

代码随想录Day 57|prim算法和kruskal算法精讲,题目:寻宝

提示&#xff1a;DDU&#xff0c;供自己复习使用。欢迎大家前来讨论~ 文章目录 图论part07**prim算法精讲**题目&#xff1a;53. 寻宝解题思路&#xff1a;Prim算法Kruskal算法总结Prim算法的核心步骤&#xff08;三部曲&#xff09;&#xff1a;关键数据结构&#xff1a;算法…

Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)

文章目录 esp-rs 简介GithubRust 包仓库Wokwi 电子模拟器开发环境Rust 环境esp-rs 环境创建 ESP32C3 项目项目结构编译项目命令运行模拟器ESP32C3 烧录 esp-rs 简介 esp-rs 是一个专注于为 Espressif 系列芯片&#xff08;如 ESP32、ESP32-S2、ESP32-C3 等&#xff09;提供 Ru…

嵌入式边缘计算软硬件开发“1+X”考证建设方案

一、引言 随着物联网、大数据、人工智能等技术的飞速发展&#xff0c;嵌入式边缘计算作为连接物理世界与数字世界的桥梁&#xff0c;其重要性日益凸显。为了适应行业对高技能人才的需求&#xff0c;推动嵌入式边缘计算技术的普及与应用&#xff0c;特制定本“1X”考证建设方案…

本地部署开源在线PPT制作与演示应用PPTist并实现异地远程使用

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 本文主要介绍如何在Windows系统环境本地部署开源在线演示文稿应用PPTist&#xff0c;并结合cpolar内网穿透工具实现随时随地远程访问与使用该项目。 PPTist …

Vue3(五) 组件通信大汇总

文章目录 一、props二、自定义事件三、mitt四、v-model1.v-model的本质2.v-model用在组件标签上3.更换modelValue4.更换modelValue时&#xff0c;可以在组件标签上多次使用v-model 五、$attrs六、$refs,与&#xffe5;parent1. 回顾标签ref属性修改组件信息2. $refs实现父修改所…

学校周赛(1)

A - Short Sort 题目&#xff1a; 思路&#xff1a; 本条题目只允许改一处地方&#xff0c;只有三个字母&#xff0c;我们可以直接枚举所有移动过的结果&#xff0c;同时使用哈希去记录其值&#xff0c;对于每一个输入我们都寻找是否有这个值记录&#xff0c;有则输出YES否则…

毕业设计选题:基于ssm+vue+uniapp的校园订餐小程序

开发语言&#xff1a;Java框架&#xff1a;ssmuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;M…

数据库管理与开发的全面解决方案——Devart全线产品测评

在现代数据库管理和应用开发中&#xff0c;拥有一套高效且强大的工具是至关重要的。Devart&#xff0c;作为一家专业提供数据库开发和管理工具的公司&#xff0c;已经在行业内树立了坚实的声誉。其产品线涵盖了从数据库连接驱动到全面的数据集成和管理解决方案&#xff0c;满足…

读数据湖仓02数据抽象

1. 不同类型的数据 1.1. 不同类型的数据在存储方面有各自的特性&#xff0c;这些特性极大地影响了数据在数据湖仓中的存储和使用方式 1.2. 结构化数据 1.2.1. 在企业等组织中&#xff0c;只有少量的数据是结构化数据 1.2.2. 结构化数据是基于事务的数据&#xff0c;是组织日…

[spring]用MyBatis XML操作数据库 其他查询操作 数据库连接池 mysql企业开发规范 动态sql

文章目录 一. MyBatis XML配置文件1. 配置链接字符串和MyBatis2. 写持久层代码方法定义Interface方法实现xml测试 3. 增删改查增:删改查 二. 开发规范(mysql)三. 其他查询操作1. 多表查询2. #{} 和 ${}(面试题)使用区别 排序功能like查询 三. 数据库连接池四. 动态sql1. < i…

【补充】倒易点阵定义

晶体点阵&#xff1a;晶体内部结构在三维空间周期平移的客观存在的数学抽象&#xff0c;反映晶体实际原子排列。 倒易点阵&#xff1a;通过对晶体的正点阵进行傅里叶变换得到的&#xff0c;其中正点阵中每个阵点的位置矢量方向代表晶面族的法向&#xff0c;位置矢量的长度是晶…

C++ | Leetcode C++题解之第442题数组中重复的数据

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> findDuplicates(vector<int>& nums) {int n nums.size();vector<int> ans;for (int i 0; i < n; i) {int x abs(nums[i]);if (nums[x - 1] > 0) {nums[x - 1] -nums[…

我把101篇公众号文章喂给了AI,终于,「小爱」可以为我代言了!

前段时间&#xff0c;搞了个微信 AI 小助理-小爱(AI)&#xff0c;爸妈玩的不亦乐乎。 零风险&#xff01;零费用&#xff01;我把AI接入微信群&#xff0c;爸妈玩嗨了&#xff0c;附教程&#xff08;下&#xff09; 目前小爱(AI)仍在持续迭代中&#xff0c;受到了很多粉丝朋友…

使用transformers中的pipeline调用huggingface中模型过程中可能遇到的问题和修改建议

使用transformers中的pipeline调用huggingface中模型过程 前言管线使用中的问题和解决huggingface的连接问题使用huggingface示例修改源继续使用pipeline No module named keras.engine 前言 HuggingFace有一个巨大的模型库&#xff0c;其中包括很多的比较成熟的经典模型&…

牛犇啊!LSTM+Transformer炸裂创新,精准度高至95.65%!

【LSTMTransformer】作为一种混合深度学习模型&#xff0c;近年来在学术界和工业界都受到了极大的关注。它巧妙地融合了长短期记忆网络&#xff08;LSTM&#xff09;在处理时序数据方面的专长和Transformer在捕捉长距离依赖关系上的优势&#xff0c;从而在文本生成、机器翻译、…

Request 原理

目录 request原理 Request继承体系 ​编辑Request获取请求行数据方法介绍 1、获取请求行数据 2、获取请求头数据 3、获取请求体数据 4、其他功能 1、获取请求参数通用方式&#xff1a; 中文乱码问题&#xff1a; 2.请求转发 3.共享数据 4.获取servletcontext reques…

set和map结构的使用

个人主页&#xff1a;敲上瘾-CSDN博客 个人专栏&#xff1a;游戏、数据结构、c语言基础、c学习、算法 目录 一、序列式容器和关联式容器 二、set和multiset 1.insert 2.erase 3.find 4.count 三、map和mapmulti 1.pair 2.insert 3.find 4.operator[ ] 5.erase 6.lo…

QT-自定义信号和槽对象树图形化开发计算器

1. 自定义信号和槽 核心逻辑&#xff1a; 需要有两个类&#xff0c;一个提供信号&#xff0c;另一个提供槽。 然后在窗口中将 信号和槽 链接起来。 示例目标&#xff1a; 创建一个 Teacher 类&#xff0c;提供信号。 创建一个 Student 类&#xff0c;提供槽。 实现步骤&…

策略路由控制选路

&#x1f423;个人主页 可惜已不在 &#x1f424;这篇在这个专栏 华为_可惜已不在的博客-CSDN博客 &#x1f425;有用的话就留下一个三连吧&#x1f63c; 目录 一、 实验拓扑 二、 实验简述 三、 实验配置 配置路由信息 配置控制选路 四、 实验验证 ​ 一、 实验…

「安装」 Windows下安装CUDA和Pytorch

「安装」 Windows下安装CUDA和Pytorch 文章目录 「安装」 Windows下安装CUDA和PytorchMac、Linux、云端Windows安装CUDA安装miniconda安装PyTorch测试总结 其他 Mac、Linux、云端 Mac、Linux、云端安装Miniconda和Pytorch的方法参考其他资料。 Windows 下面进行Windows下安装…