贪心(Greedy)
- 贪心策略,也称为贪婪策略
定义
:每一步都采取当前状态下最优的选择(局部最优解),从而希望推导出全局最优解- 贪心的应用
哈夫曼树
最小生成树算法:Prim、Kruskal
最短路径算法:Dijkstra
问题1:最优装载
贪心策略:每一次都优先选择重量最小的古董
① 选择重量为 2 的古董,剩重量 28
② 选择重量为 3 的古董,剩重量 25
③ 选择重量为 4 的古董,剩重量 21
④ 选择重量为 5 的古董,剩重量 16
⑤ 选择重量为 7 的古董,剩重量 9
根据上面的选择,最多能装载 5 个古董
问题2:零钱兑换
假设有 25 分、10 分、5 分、1 分的硬币,现要找给客户 41 分的零钱,如何办到硬币个数最少?
贪心策略: 每一次都优先选择面值最大的硬币
① 选择 25 分的硬币,剩 16 分
② 选择 10 分的硬币,剩 6 分
③ 选择 5 分的硬币,剩 1 分
④ 选择 1 分的硬币
最终的解是共 4 枚硬币
25 分、10 分、5 分、1 分硬币各一枚
贪心策略的缺陷(换零钱2)
贪心注意点
贪心策略并不一定能得到全局最优解
因为一般没有测试所有可能的解,容易过早做决定,所以没法达到最佳解
贪图眼前局部的利益最大化,看不到长远未来,走一步看一步
优点:简单、高效、不需要穷举所有可能,通常作为其他算法的辅助算法来使用
缺点:鼠目寸光,不从整体上考虑其他可能,每次采取局部最优解,不会再回溯,因此很少情况会得到最优解
0-1背包问题
0-1背包:每件物品最多只能选择一件
0-1 背包 - 实例
package 贪心算法.背包问题;
import java.util.Arrays;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class Knapsack {
public static void main(String[] args) {
select("价值主导", (Article a1, Article a2) -> {
// 价值大的优先
return a2.value - a1.value;
});
select("重量主导", (Article a1, Article a2) -> {
// 重量小的优先
return a1.weight - a2.weight;
});
select("价值密度主导", (Article a1, Article a2) -> {
// 价值密度大的优先
return Double.compare(a2.valueDensity, a1.valueDensity);
});
}
static void select(String title,Comparator<Article> cmp) {
Article[] articles = new Article[]{
new Article(35, 10), new Article(30, 40),
new Article(60, 30), new Article(50, 50),
new Article(40, 35), new Article(10, 40),
new Article(25, 30)
};
//1.容量
int capacity = 150;
int weight = 0;
int value = 0;
//2.贪心 排序
Arrays.sort(articles, cmp); //按照value降序
//3.容器 装 选出来的article
List<Article> selectedArticles = new LinkedList<>();
//4.贪心算法
for (int i = 0; i < articles.length && weight < capacity; i++) {
int newWeight = weight + articles[i].weight;
if(newWeight < capacity){
weight = newWeight;
value += articles[i].value;
selectedArticles.add(articles[i]);
}
}
System.out.println("【价值主导】");
System.out.println("总价值 =" + value);
System.out.println(selectedArticles);
System.out.println("---------------");
}
}