贪心算法
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。
贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
案例:广播台集合覆盖问题
如何选择最少的广播台,让所有地区都可以收到信号?
. 思路分析
-
用一个arealist集合存储地区:“北京”、“上海”、“天津”、“广州”、“深圳”、“成都”、“杭州”、“大连”
-
用一个hashmap存储广播台和覆盖范围的关系:HashMap<String, HashSet<String>>
-
用一个keylist集合存储符合要求的电台
-
遍历hashmap,每个key值对应的value与list集合做交集,交集的结果用key变量将电台记录下来,每次遍历都会有一个交集结果,每次就将本次的交集结果与key中的交集结果进行比较,大的就重新赋值key值,遍历完之后,就会得到本轮完整遍历的与arealist集合交集最多的那个电台,然后将该电台加入到keylist集合中,再将arealist中移除与该电台覆盖地区相同的地区。然后进入下一轮遍历hashmap,继续如上操作。
直到arealist中无任何数据,结束。
-
此时,keylist中存放的就是最终结果了。
贪心算法的核心就是,每次遍历都会获取到最优或最有利的值。
. 代码实现
public class Greedy {
public static void main(String[] args) {
// 存储需要覆盖的所有城市
ArrayList<String> areaList = new ArrayList<>();
areaList.add("北京");
areaList.add("上海");
areaList.add("天津");
areaList.add("广州");
areaList.add("深圳");
areaList.add("成都");
areaList.add("杭州");
areaList.add("大连");
// 存储每个电台覆盖的城市
HashSet<String> broadcastArea1 = new HashSet<>();
broadcastArea1.add("北京");
broadcastArea1.add("上海");
broadcastArea1.add("天津");
HashSet<String> broadcastArea2 = new HashSet<>();
broadcastArea2.add("广州");
broadcastArea2.add("北京");
broadcastArea2.add("深圳");
HashSet<String> broadcastArea3 = new HashSet<>();
broadcastArea3.add("成都");
broadcastArea3.add("上海");
broadcastArea3.add("杭州");
HashSet<String> broadcastArea4 = new HashSet<>();
broadcastArea4.add("上海");
broadcastArea4.add("天津");
HashSet<String> broadcastArea5 = new HashSet<>();
broadcastArea5.add("杭州");
broadcastArea5.add("大连");
// 将电台与其所覆盖的城市关联起来,成为键值对
HashMap<String, HashSet<String>> broadcasts = new HashMap<>();
broadcasts.put("K1",broadcastArea1);
broadcasts.put("K2",broadcastArea2);
broadcasts.put("K3",broadcastArea3);
broadcasts.put("K4",broadcastArea4);
broadcasts.put("K5",broadcastArea5);
// keyList用于存储符合要求的电台
ArrayList<String> keyList = new ArrayList<>();
// key用于存储每一轮遍历与areaList交集最多的电台
String key = null;
// 用于临时存储电台覆盖的城市
HashSet<String> temp = new HashSet<>();
// 结束条件是areaList没有任何数据,即说明每个城市都被覆盖了
while (!areaList.isEmpty()){
// 遍历broadcasts
for (String broadcastKey : broadcasts.keySet()) {
temp.clear(); // 一定要清空,temp是临时存储的变量
// 将当前电台覆盖的城市添加到临时存储的变量中
temp.addAll(broadcasts.get(broadcastKey));
// temp集合与areaList集合取交集,交集结果赋值给temp
temp.retainAll(areaList);
// 判断temp的大小是否大于0,以及key值是否为空,
// 如果不为空就判断本轮的交集大小是否大于上一次存储的key值对应的电台覆盖的城市,大于就重新赋值新的最优的结果(贪心算法核心部分)
if (temp.size() > 0 && (key==null || temp.size() > broadcasts.get(key).size())){
key = broadcastKey;
}
}
// 一轮遍历完之后,判断key是否为空,不为空,就将key存入到keyList集合中,表示符合要求的电台
// 最后将符合要求的电台所覆盖的城市,从areaList集合中移除掉,说明这些城市已被覆盖
if (key!=null){
keyList.add(key);
areaList.removeAll(broadcasts.get(key));
key = null;
}
}
// 输出结果
System.out.println(keyList.toString());
}
}
结果:
[K1, K2, K3, K5]