贪心算法是一种寻找局部最优解并希望通过这种方式获得全局最优解的算法策略。它在每个子问题上都做出最优的选择,通常不能保证得到全局最优解,但对某些问题确实可以达到全局最优解。
找零问题
假设我们有面额为 25、10、5 和 1 的硬币,我们想用最少的硬币数找零给客户。
function coinChange(coins, amount) {
// 1. 对硬币进行降序排序,确保从最大面额开始贪心
coins.sort((a, b) => b - a);
let result = 0;
for (let coin of coins) {
// 2. 使用当前面额的硬币,直到不能再使用为止
while (amount >= coin) {
amount -= coin;
result++;
}
// 3. 如果已经找完零,直接退出
if (amount === 0) break;
}
// 如果还没找完零,则无法找零
return amount !== 0 ? -1 : result;
}
console.log(coinChange([25, 10, 5, 1], 63)); // 输出 6,因为使用 2x25,1x10,3x1
常见的贪心算法题型包括:
- 区间选择问题:例如,给定多个区间,选择尽量多的不重叠的区间。
- 硬币找零问题:如上述示例。
- 背包问题的贪心版本:有时,与完整的动态规划解相比,贪心算法可能只给出近似解。
- 任务调度问题:例如,给定一组任务和它们的截止时间,尽量在截止时间之前完成尽可能多的任务。
- 霍夫曼编码:用于数据压缩的算法。
- 最小生成树算法,如 Kruskal 和 Prim 算法。
当考虑贪心策略时,关键是证明该策略确实能够得到问题的最优解。不是所有问题都适合贪心算法,但对于那些适合的问题,贪心算法通常是最有效的。
不是所有的最优化问题都能使用贪心算法来解决,但在某些特定情况下,贪心算法是最好的选择。
- 问题具有贪心选择属性:可以从局部最优解推导出全局最优解。换句话说,一个全局最优解可以通过一系列的局部最优选择来构建。
- 问题具有最优子结构:问题的最优解可以从其子问题的最优解中构造。这意味着我们可以递归地或迭代地解决问题,每次选择一个局部最优解。
- 无后效性:在进行选择后,这个选择不会影响后续的选择。
- 可以通过排序简化问题:很多时候,问题可以通过对输入数据进行排序后更容易地应用贪心策略。
- 适用于近似算法:对于某些问题,获取精确解可能非常困难或者计算成本非常高。在这种情况下,使用贪心算法可以快速得到一个相对好的近似解。