# 贪心算法基础
# 简介
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最好或最优的选择,从而希望导致结果是最好或最优的算法。
# 基本思想
- 建立数学模型来描述问题
- 把求解的问题分成若干个子问题
- 对每个子问题求解,得到子问题的局部最优解
- 把子问题的解局部最优解合成原来问题的一个解
# 适用条件
- 最优子结构:问题的最优解包含子问题的最优解
- 局部最优解能导致全局最优解:每一步的最优解能够导致最终的最优解
# 经典问题
# 1. 找零钱问题
function makeChange(amount, coins) {
coins.sort((a, b) => b - a); // 从大到小排序
let result = [];
let remaining = amount;
for (let coin of coins) {
while (remaining >= coin) {
result.push(coin);
remaining -= coin;
}
}
return result;
}
# 2. 区间调度问题
function intervalScheduling(intervals) {
// 按结束时间排序
intervals.sort((a, b) => a[1] - b[1]);
let result = [intervals[0]];
let lastEnd = intervals[0][1];
for (let i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= lastEnd) {
result.push(intervals[i]);
lastEnd = intervals[i][1];
}
}
return result;
}
# 优点与缺点
# 优点
- 简单直观
- 解题速度快
- 适用于子问题相互独立的问题
# 缺点
- 不能保证得到最优解
- 可能得到次优解
- 不能用来解决所有问题
# 应用场景
- 最小生成树算法(Prim算法、Kruskal算法)
- 单源最短路径(Dijkstra算法)
- 哈夫曼编码
- 任务调度
- 背包问题的近似解
# 实现步骤
- 创建数学模型来描述问题
- 定义贪心策略
- 证明贪心策略的正确性
- 实现算法
- 分析算法的时间复杂度和空间复杂度
# 复杂度分析
- 时间复杂度:取决于具体问题,通常为O(n)或O(nlogn)
- 空间复杂度:通常为O(1)或O(n)
# 注意事项
- 在使用贪心算法之前,需要证明贪心选择性质
- 贪心算法不一定能得到最优解
- 有时需要与其他算法(如动态规划)进行比较
- 在某些情况下可以作为其他算法的启发式方法
# 练习建议
- 从简单的问题开始,如找零钱问题
- 理解每个问题的贪心策略
- 尝试证明贪心策略的正确性
- 多做题,积累不同类型的贪心问题