一、基本概念
贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解。
二、算法步骤
贪心算法一般按照如下步骤进行:
- 建立数学模型来描述问题
- 把待求解的问题分解位若干个子问题
- 对每个子问题进行求解,得到局部最优解
- 把子问题的局部最优解合并为原问题的一个解
简单来说,就是在进行每一步选择时,都基于当前步,选择最优的解。
这里我们以一个简单的集合覆盖问题为例:
假设现有如下几个广播台以及广播信号可以覆盖的地区,如何选择最少的广播,使广播信号可以覆盖所有地区。
由上题可以得知,现有{北京、上海、天津、广州、深圳、成都、杭州、大连}这几个地区需要覆盖,那么我们在进行选择时,就是每一次尽量覆盖足够多的未被覆盖的地区。
- 选择K1,那么此时还有{广州、深圳、成都、杭州、大连}未覆盖
- 选择K2,那么此时还有{成都、杭州、大连}未覆盖
- 选择K3,那么此时还有{大连}未覆盖
- 现在K5,此时已经全部覆盖
三、贪心算法解决集合覆盖问题代码实现
/**
* @author dankejun
* @create 2020/9/1816:57
*/
public class GreedyAlgorithm {
public static void main(String[] args) {
HashMap<String, HashSet<String>> map = new HashMap<>();
HashSet<String> hashSet1 = new HashSet();
hashSet1.add("北京");
hashSet1.add("上海");
hashSet1.add("天津");
HashSet<String> hashSet2 = new HashSet();
hashSet2.add("广州");
hashSet2.add("北京");
hashSet2.add("深圳");
HashSet<String> hashSet3 = new HashSet();
hashSet3.add("成都");
hashSet3.add("上海");
hashSet3.add("杭州");
HashSet<String> hashSet4 = new HashSet();
hashSet4.add("上海");
hashSet4.add("天津");
HashSet<String> hashSet5 = new HashSet();
hashSet5.add("杭州");
hashSet5.add("大连");
map.put("k1", hashSet1);
map.put("k2", hashSet2);
map.put("k3", hashSet3);
map.put("k4", hashSet4);
map.put("k5", hashSet5);
List<String> greedy = greedy(map);
System.out.println("选择:" + greedy);
}
public static List<String> greedy(HashMap<String, HashSet<String>> map) {
//将所有需要覆盖的地区放到allAreas中
HashSet<String> allAreas = new HashSet<>();
Iterator<Map.Entry<String, HashSet<String>>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, HashSet<String>> next = iterator.next();
allAreas.addAll(next.getValue());
}
//对每个电台进行贪心选择
List<String> selects = new ArrayList<>();//存放选择的电台号的集合
HashSet<String> tempSet = new HashSet<>();//中间变量,用于求交集
String maxKey = null;//存放具有最优选择的电台号
while (allAreas.size() != 0) {//还未被全部覆盖
maxKey = null;//重置maxKey
for (String key : map.keySet()) {
tempSet.clear();//清空临时集合
HashSet<String> areas = map.get(key);
tempSet.addAll(areas);
tempSet.retainAll(allAreas);//求出两个集合的交集,赋值给tempSet
//如果当前电台覆盖地区数量大于maxKey,就用当前电台号替换maxKey
if (tempSet.size() > 0 && (maxKey == null || tempSet.size() > map.get(maxKey).size())) {
maxKey = key;
}
}
//将当前最优选择加入集合,并在全部地区中删除
if (maxKey != null) {
selects.add(maxKey);
allAreas.removeAll(map.get(maxKey));
}
}
return selects;
}
}
测试结果: