news 2026/4/15 18:27:13

【数据结构与算法】第48篇:算法思想(三):贪心算法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【数据结构与算法】第48篇:算法思想(三):贪心算法

目录

一、什么是贪心算法

1.1 贪心策略

1.2 贪心 vs 动态规划

二、经典问题一:活动选择问题

2.1 问题描述

2.2 贪心证明思路

2.3 代码实现

2.4 复杂度分析

三、经典问题二:找零问题

3.1 问题描述

3.2 贪心的局限性

3.3 代码实现(贪心)

四、贪心算法的正确性条件

4.1 贪心选择性质

4.2 最优子结构

五、其他贪心算法经典问题

六、贪心算法解题模板

七、贪心 vs DP 选择指南

八、小结

九、思考题


一、什么是贪心算法

1.1 贪心策略

贪心算法在每一步都选择当前状态下的最优解(局部最优),而不考虑未来的影响。

特点

  • 实现简单,通常很快

  • 不能保证对所有问题都得到全局最优解

  • 需要证明问题具有贪心选择性质

1.2 贪心 vs 动态规划

对比项贪心算法动态规划
决策方式单次决策,不回头考虑所有可能性
子问题重叠不一定必然
典型问题活动选择、找零背包、LCS
时间复杂度通常 O(n) 或 O(n log n)通常 O(n²) 或更高
全局最优保证需要证明总是保证

二、经典问题一:活动选择问题

2.1 问题描述

有 n 个活动,每个活动有开始时间s[i]和结束时间f[i]。活动不能同时进行(时间不能重叠)。求最多能安排多少个活动。

贪心策略:每次选择结束时间最早且与已选活动不冲突的活动。

2.2 贪心证明思路

为什么选结束时间最早的是最优?

  • 选结束时间最早的活动,能为剩余活动留出最多的时间

  • 这个选择不会比任何最优解差

2.3 代码实现

c

#include <stdio.h> #include <stdlib.h> // 活动结构体 typedef struct { int start; int finish; } Activity; // 按结束时间排序(升序) int cmp(const void *a, const void *b) { return ((Activity*)a)->finish - ((Activity*)b)->finish; } // 贪心算法选择活动 int activitySelection(Activity activities[], int n) { if (n == 0) return 0; // 1. 按结束时间排序 qsort(activities, n, sizeof(Activity), cmp); // 2. 贪心选择 int count = 1; int lastFinish = activities[0].finish; printf("选中的活动: (0, %d)\n", activities[0].finish); for (int i = 1; i < n; i++) { if (activities[i].start >= lastFinish) { printf("选中的活动: (%d, %d)\n", activities[i].start, activities[i].finish); count++; lastFinish = activities[i].finish; } } return count; } int main() { Activity activities[] = { {1, 4}, {3, 5}, {0, 6}, {5, 7}, {3, 9}, {5, 9}, {6, 10}, {8, 11}, {8, 12}, {2, 14}, {12, 16} }; int n = sizeof(activities) / sizeof(activities[0]); int result = activitySelection(activities, n); printf("最多可安排活动数: %d\n", result); return 0; }

运行结果:

text

选中的活动: (0, 4) 选中的活动: (5, 7) 选中的活动: (8, 11) 选中的活动: (12, 16) 最多可安排活动数: 4

2.4 复杂度分析

  • 排序:O(n log n)

  • 贪心选择:O(n)

  • 总时间复杂度:O(n log n)


三、经典问题二:找零问题

3.1 问题描述

给定面额数组coins(假设无限供应)和金额amount,求最少需要多少枚硬币。

贪心策略:每次用最大面额不超过剩余金额的硬币。

3.2 贪心的局限性

贪心算法不一定对所有硬币系统都正确。

硬币系统金额贪心结果最优结果是否正确
1,5,10,25(标准)3025+5 → 2枚10+10+10 → 3枚
1,3,464+1+1 → 3枚3+3 → 2枚

3.3 代码实现(贪心)

c

#include <stdio.h> #include <stdlib.h> // 按面额降序排序 int cmpDesc(const void *a, const void *b) { return *(int*)b - *(int*)a; } // 贪心找零 void greedyCoinChange(int coins[], int n, int amount) { qsort(coins, n, sizeof(int), cmpDesc); printf("贪心找零 %d 元: ", amount); int remaining = amount; for (int i = 0; i < n && remaining > 0; i++) { if (remaining >= coins[i]) { int count = remaining / coins[i]; printf("%d枚%d元 ", count, coins[i]); remaining -= count * coins[i]; } } if (remaining > 0) { printf("无法找零"); } printf("\n"); } int main() { int coins1[] = {1, 5, 10, 25}; int coins2[] = {1, 3, 4}; greedyCoinChange(coins1, 4, 30); greedyCoinChange(coins2, 3, 6); return 0; }

运行结果:

text

贪心找零 30 元: 1枚25元 1枚5元 贪心找零 6 元: 1枚4元 2枚1元

四、贪心算法的正确性条件

4.1 贪心选择性质

定义:问题的全局最优解可以通过一系列局部最优选择(贪心选择)得到。

证明方法

  • 假设有一个最优解

  • 证明可以将其改造为包含贪心选择的解

  • 用数学归纳法或反证法

4.2 最优子结构

定义:问题的最优解包含子问题的最优解。

活动选择问题满足最优子结构:去掉贪心选择的活动后,剩余子问题的最优解加上这个活动就是全局最优解。


五、其他贪心算法经典问题

问题贪心策略时间复杂度
哈夫曼编码每次合并频率最小的两个节点O(n log n)
最小生成树(Prim/Kruskal)每次选最小权边O(E log V)
最短路径(Dijkstra)每次选距离最小的顶点O(V²) 或 O(E log V)
区间覆盖每次选能覆盖最远右端点的区间O(n log n)
任务调度(最小化总完成时间)按处理时间升序O(n log n)

六、贪心算法解题模板

c

// 贪心算法通用框架 void greedy() { // 1. 按某种规则排序(如果需要) sort(data); // 2. 初始化结果 result = initial; // 3. 贪心选择 for (each element in sorted order) { if (符合条件) { 选择当前元素; 更新状态; } } }

七、贪心 vs DP 选择指南

问题特征推荐算法
可以证明贪心选择性质贪心(更高效)
需要考虑多种可能性DP
子问题重叠明显DP
只需局部最优决策贪心
可分解为独立子问题分治或贪心

判断方法

  1. 尝试贪心策略,看能否举出反例

  2. 如果不能,考虑DP

  3. 如果DP太慢,尝试证明贪心正确


八、小结

这一篇我们学习了贪心算法:

要点说明
核心思想每次选当前最优,希望达到全局最优
活动选择选结束时间最早的,复杂度 O(n log n)
找零问题用最大面额优先,但不一定正确
正确性条件贪心选择性质 + 最优子结构
适用范围图论(MST、最短路)、调度、哈夫曼编码

贪心 vs DP

  • 贪心:局部最优 → 全局最优(需要证明)

  • DP:考虑所有可能性 → 全局最优(保证正确)

学习建议

  • 多练习判断问题是否适合贪心

  • 学会举反例推翻错误贪心策略

  • 掌握常见贪心问题(活动选择、哈夫曼、区间覆盖)

下一篇我们讲代码调试技巧与常见内存错误排查。


九、思考题

  1. 活动选择问题中,如果改成选开始时间最早或持续时间最短,能得到最优解吗?为什么?

  2. 硬币系统 {1, 5, 10, 25} 中贪心总是正确的,但 {1, 3, 4} 中不对。硬币系统满足什么条件时贪心正确?

  3. 如何用贪心算法解决“用最少的箭射爆所有气球”问题?

  4. 如果活动有时间权重(每个活动有不同价值),如何用DP解决?

欢迎在评论区讨论你的答案。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 18:22:36

郭老师-心力:比能力更重要的核心竞争力

心力&#xff1a;比能力更重要的核心竞争力 ——从自我认同到复原力的修炼之路“一个人的心力比能力更重要&#xff0c; 心力不强&#xff0c;有能力也发挥不出来&#xff1b; 而心力强大&#xff0c;能力稍弱也能成长起来。”&#x1f33f; 心力是你内心的力量&#xff0c; 它…

作者头像 李华
网站建设 2026/4/15 18:21:36

【仅剩72小时解密权限】:2026奇点大会《多模态质检可信白皮书》核心章节泄露——含13家头部制造企业真实误判案例与可审计溯源链设计

第一章&#xff1a;多模态工业质检的范式跃迁与可信危机 2026奇点智能技术大会(https://ml-summit.org) 传统工业质检长期依赖单一视觉模态与人工经验判据&#xff0c;其鲁棒性在面对微米级缺陷、低对比度纹理、跨产线泛化等场景时持续承压。近年来&#xff0c;融合可见光、红…

作者头像 李华
网站建设 2026/4/15 18:15:43

微信小程序调用云端AI:集成PyTorch 2.8模型提供智能服务

微信小程序调用云端AI&#xff1a;集成PyTorch 2.8模型提供智能服务 1. 场景与痛点分析 想象一下&#xff0c;你正在开发一个微信小程序&#xff0c;需要实现图片风格转换或文本摘要功能。直接在手机端运行复杂的AI模型显然不现实——计算资源有限、模型体积大、耗电快。这时…

作者头像 李华
网站建设 2026/4/15 18:13:25

AI做图打造活人感:关键词+微调实用技巧 出图真实不生硬

很多零基础做自媒体的朋友都跟我吐槽&#xff0c;用AI做出来的人物图假得离谱&#xff0c;要么脸歪眼斜&#xff0c;要么完美得像塑料蜡像&#xff0c;半点儿活人气息都没有&#xff0c;做封面没人点&#xff0c;做海报没人信&#xff0c;其实根本不用什么高深技巧&#xff0c;…

作者头像 李华
网站建设 2026/4/15 18:11:23

终极窗口分辨率自定义指南:SRWE如何让创意工作者突破显示限制

终极窗口分辨率自定义指南&#xff1a;SRWE如何让创意工作者突破显示限制 【免费下载链接】SRWE Simple Runtime Window Editor 项目地址: https://gitcode.com/gh_mirrors/sr/SRWE 想象一下&#xff0c;你正在制作一个需要适配多种屏幕尺寸的UI设计稿&#xff0c;或者想…

作者头像 李华
网站建设 2026/4/15 18:10:50

GLM-4.1V-9B-Base创意无限:基于MATLAB算法仿真的AI艺术生成联动

GLM-4.1V-9B-Base创意无限&#xff1a;基于MATLAB算法仿真的AI艺术生成联动 1. 科学与艺术的奇妙碰撞 当严谨的数学计算遇上天马行空的AI想象力&#xff0c;会擦出怎样的火花&#xff1f;我们尝试将MATLAB生成的科学可视化图像输入GLM-4.1V-9B-Base模型&#xff0c;让AI为这些…

作者头像 李华