贪心算法详解:原理、应用场景及代码实现
创作时间:
作者:
@小白创作中心
贪心算法详解:原理、应用场景及代码实现
引用
CSDN
1.
https://blog.csdn.net/2301_79602614/article/details/138244968
贪心算法是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望导致结果是最好或最优的算法。这种算法通常用于求解优化问题,如最小生成树、背包问题等。
贪心算法的基本概念
贪心算法的核心思想可以概括为以下三个步骤:
- 将寻找最优解的问题分为若干个步骤
- 每一步骤都采用贪心原则,选取当前最优解
- 因为没有考虑所有可能,局部最优的堆叠不一定让最终解最优
贪心算法的应用场景包括:
- 背包问题:给定一组物品和一个背包,每个物品有一定的重量和价值,要求在不超过背包容量的情况下,尽可能多地装入物品。
- 活动选择问题:在一个活动集合中,每次只能参加一个活动,问如何安排时间以最大化所有活动的收益。
- 编辑距离问题:给定两个字符串,求它们之间的最小编辑距离(即将一个字符串转换为另一个字符串所需的最少操作次数)。
- 网络流问题:给定一张有向图和一些起点和终点,求最大流量。
- 找零问题:给定一定数量的硬币和需要找零的金额,求使用最少的硬币数。
贪心算法的常见问题及解答
- 贪心算法一定会找到最优解吗?
- 答:不一定。贪心算法只保证在每一步选择中都是最优的,但并不能保证整个问题的最优解。例如,背包问题中的贪心算法可能会导致最后一个物品没有被装入背包。
- 如何判断一个问题是否适合用贪心算法解决?
- 答:一个问题如果可以用递归的方式分解成若干个子问题,且每个子问题都有明确的最优解(即局部最优),那么这个问题就可以用贪心算法解决。
- 贪心算法的时间复杂度是多少?
- 答:贪心算法的时间复杂度取决于问题的规模和具体实现。一般来说,对于规模较小的问题,贪心算法的时间复杂度可以达到O(nlogn)或O(n^2);对于规模较大的问题,可能需要O(n^3)或更高。
贪心算法的具体实现
Dijkstra算法
while (!list.isEmpty()) {
// 选取当前【距离最小】的顶点
Vertex curr = chooseMinDistVertex(list);
// 更新当前顶点邻居距离
updateNeighboursDist(curr);
// 移除当前顶点
list.remove(curr);
// 标记当前顶点已经处理过
curr.visited = true;
}
需要注意的是,当图中存在负边时,Dijkstra算法可能无法得到正确的最短路径解。这是因为贪心原则会认为本次已经找到了该顶点的最短路径,下次不会再处理它(curr.visited = true)。相比之下,Bellman-Ford算法不会出错,因为它每次都处理所有边,但效率不如Dijkstra算法。
Prim算法
while (!list.isEmpty()) {
// 选取当前【距离最小】的顶点
Vertex curr = chooseMinDistVertex(list);
// 更新当前顶点邻居距离
updateNeighboursDist(curr);
// 移除当前顶点
list.remove(curr);
// 标记当前顶点已经处理过
curr.visited = true;
}
Kruskal算法
while (list.size() < size - 1) {
// 选取当前【距离最短】的边
Edge poll = queue.poll();
// 判断两个集合是否相交
int i = set.find(poll.start);
int j = set.find(poll.end);
if (i != j) { // 未相交
list.add(poll);
set.union(i, j); // 相交
}
}
其他贪心算法的例子
- 选择排序、堆排序
- 拓扑排序
- 并查集合中的 union by size 和 union by height
- 哈夫曼编码
- 钱币找零(change-making problem)
- 任务编排
- 求复杂问题的近似解
零钱兑换问题
零钱兑换II(LeetCode 518)
这是一个典型的动态规划问题,但也可以用贪心算法来尝试解决。下面是一个暴力递归的实现:
public int rec(int index, int[] coins, int remainder) {
// 1.情况1:剩余金额 < 0 - 无解
// 2.情况2:剩余金额 > 0 - 继续递归
// 3.情况3:剩余金额 = 0 - 有解
if (remainder < 0) {
return 0;
} else if (remainder == 0) {
return 1;
} else {
int count = 0;
for (int i = index; i < coins.length; i++) {
count += rec(i, coins, remainder - coins[i]);
}
return count;
}
}
但是这个代码在LeetCode上运行会超时,因为重复处理了很多次相同的操作。我们可以考虑用记忆化搜索或动态规划来优化。
零钱兑换(LeetCode 322)
这是一个求最小硬币数的问题,可以用动态规划来解决。下面是一个暴力递归的实现:
static int min = -1; // 需要的最少硬币数 2 3
public int coinChange(int[] coins, int amount) {
rec(0, coins, amount, new AtomicInteger(-1), new LinkedList<>(), true);
return min;
}
// count 代表某一组合 钱币的总数 可变的整数对象
public void rec(int index, int[] coins, int remainder, AtomicInteger count, LinkedList<Integer> stack, boolean first) {
if (!first) {
stack.push(coins[index]);
}
count.incrementAndGet(); // count++
if (remainder == 0) {
System.out.println(stack);
if (min == -1) {
min = count.get();
} else {
min = Integer.min(min, count.get());
}
} else if (remainder > 0) {
for (int i = index; i < coins.length; i++) {
rec(i, coins, remainder - coins[i], count, stack, false);
}
}
count.decrementAndGet(); // count--
if (!stack.isEmpty()) {
stack.pop();
}
}
贪心法求解零钱兑换(LeetCode 322)
贪心法在某些情况下可以快速得到解,但并不保证总是能得到最优解。下面是一个贪心算法的实现:
public int coinChange(int[] coins, int amount) {
int remainder = amount;
int count = 0;
for (int coin : coins) {
while (remainder - coin > 0) {
remainder -= coin;
count++;
}
if (remainder - coin == 0) {
remainder = 0;
count++;
break;
}
}
if (remainder > 0) {
return -1;
} else {
return count;
}
}
需要注意的是,贪心算法在某些情况下可能会得到错误的解,因为它没有考虑所有可能的组合。例如,对于输入[15, 10, 1]和金额21,贪心算法会得到错误的解,因为它没有“回头”机制来检查更优的组合。
热门推荐
九江学院附属医院救治多例电击伤患者 被电击伤如何自救互救?
玉灵膏的配方比例与功效:龙眼肉和西洋参的黄金配比
离婚后办理不动产证需要什么资料
如何通过饮食调理气血?推荐这些食物
北京市十大旅游景点
电子背散射衍射技术(EBSD):原理、应用及样品制备详解
会议系统设备不兼容问题全面解析:从硬件到软件探索高效解决方案
软件升级后兼容性问题及解决方案探讨
职场加薪的六点要求:提升自己的价值与谈判力
教你几招如何应对身边那些“恶心奇葩”的同事
行人交通安全,不容忽视
电动车骑行安全指南:从交通规则到充电安全全方位防护
出版社编辑是做什么的工作
雷达信号处理中的恒虚警检测(CFAR)技术详解
US Crude Oil Prices Data|原油价格数据集|市场分析数据集
活塞环在不同应用中的特性
一键启动电池没电怎么办?处理方法与预防措施全攻略
内蒙古地区在民国时期为何被一分为四
三清山超详细旅游攻略,拿走不谢!
从 CUDA 对 AI 芯片思考
以“精准服务”为笔,绘就营商环境新图景!九里亭街道深入推进“服务包”制度
反思智能体:提升AI决策能力的利器
反馈+反思=加速成长
海南十大必玩景点全攻略!假期这样玩才过瘾,最后一个太惊艳!
《熵减法则》:对抗无序的方法论
3月LPR出炉,已连续5个月维持不变,释放什么信号
河北方言历史:老乡见老乡,说话听不懂,如何认识你的老乡?
普通话标准音,为何从河北滦平县采集,而不是北京?
尊龙:从孤儿到“亚洲第一美男”的传奇人生
窃密木马借"壁纸引擎"传播,Steam "再中招"