贪心算法
贪心算法是指在对问题进行求解时,在每一步选择中都采取最好或者最优的选择,从而希望能够导致结果是最好或者最优的算法。
1.应用场景–集合覆盖问题
假设存在下面需要付费的广播台,以及广播台信号可以覆盖的地区。如何选择最少的广播台,让所有的地区都可以接收到信号。
2.思路
遍历所有的广播电台,找到一个覆盖了最多未覆盖的地区的电台,将这个电台加入到一个集合中,并想办法把该电台覆盖的地区在下次比较时去掉
public class GreedyAlgorithm {
public static void main(String[] args) {
//创建广播电台,放入到Map
HashMap<String,HashSet<String>> broadcasts = new HashMap<String, HashSet<String>>();
//将各个电台放入到broadcasts
HashSet<String> hashSet1 = new HashSet<String>();
hashSet1.add("北京");
hashSet1.add("上海");
hashSet1.add("天津");
HashSet<String> hashSet2 = new HashSet<String>();
hashSet2.add("广州");
hashSet2.add("北京");
hashSet2.add("深圳");
HashSet<String> hashSet3 = new HashSet<String>();
hashSet3.add("成都");
hashSet3.add("上海");
hashSet3.add("杭州");
HashSet<String> hashSet4 = new HashSet<String>();
hashSet4.add("上海");
hashSet4.add("天津");
HashSet<String> hashSet5 = new HashSet<String>();
hashSet5.add("杭州");
hashSet5.add("大连");
//加入到map
broadcasts.put("k1",hashSet1);
broadcasts.put("k2",hashSet2);
broadcasts.put("k3",hashSet3);
broadcasts.put("k4",hashSet4);
broadcasts.put("k5",hashSet5);
// allAreas存放所有地区
HashSet<String> allAreas = new HashSet<String>();
allAreas.add("北京");
allAreas.add("上海");
allAreas.add("天津");
allAreas.add("广州");
allAreas.add("深圳");
allAreas.add("成都");
allAreas.add("杭州");
allAreas.add("大连");
//创建一个ArrayList,存放选择的电台集合
ArrayList<String> selects = new ArrayList<String>();
//定义一个临时的集合,在遍历的过程中,存放遍历过程中的电台和当前还没有覆盖的地区的交集
HashSet<String> tempSet = new HashSet<String>();
//定义一个maxKey,保存在一次遍历中,能够覆盖最大未覆盖的地区对应的电台的key,maxKey!=null,添加到selects
String maxKey = null;
while (allAreas.size() != 0){//表示还没有覆盖到所有地区
//没进行依次while
maxKey = null;
//遍历broadcasts
for (String key: broadcasts.keySet()){
//每进行一次for循环
tempSet.clear();
//当前这个key能够覆盖的地区
HashSet<String> areas = broadcasts.get(key);
tempSet.addAll(areas);
//求出tempSet。allAreas的交集,交集会赋给tempSet
tempSet.retainAll(allAreas);
//如果当前这个集合包含的未覆盖地区的数量,比maxKey指向的集合地区还多
//就需要重置maxKey
//贪心算法的特点tempSet.size() > broadcasts.get(maxKey).size()),每次选择最优的
if (tempSet.size() > 0 && (maxKey == null || tempSet.size() > broadcasts.get(maxKey).size())){
maxKey = key;
}
}
if (maxKey != null){
selects.add(maxKey);
//将maxKey指向的广播电台覆盖的地区从allAreas去掉
allAreas.removeAll(broadcasts.get(maxKey));
}
}
System.out.println("得到的最终结果是" + selects);
}
}
普利姆算法
1.修路问题
2.最小生成树MST
给定一个带权的无向连通图,如何选取一棵生成树,使树上所有边上权的总和为最小,这叫最小生成树。N个顶点,一定有N-1个边;包含全部顶点,N-1条边都在图中
public class PrimAlgorithm {
public static void main(String[] args) {
//测试看图是否创建成功
char[] data = new char[]{'A','B','C','D','E','F','G'};
int verxs = data.length;
//邻接矩阵的关系使用二维数组表示
int [][] weight = new int[][]{
{10000,5,7,10000,10000,10000,2},
{5,100000,10000,9,100000,100000,3},
{7,100000,10000,10000,8,100000,100000},
{100000,9,10000,10000,100000,4,100000},
{100000,10000,8,10000,100000,5,4},
{100000,10000,10000,4,5,100000,6},
{2,3,100000,10000,4,6,100000},};
//创建MGraph对象
MGraph graph = new MGraph(verxs);
MinTree minTree = new MinTree();
minTree.createGraph(graph, verxs, data,weight);
//输出
minTree.showGraph(graph);
//测试普利姆算法
minTree.prim(graph,1);
}
}
//创建最小生成树-》村庄的图
class MinTree{
//创建图的邻接矩阵
/**
*
* @param graph 图对象
* @param verxs 图对应的顶点个数
* @param data 图的各个顶点的值
* @param weight 图的邻接矩阵
*/
public void createGraph(MGraph graph, int verxs, char data[], int[][] weight){
int i,j;
for ( i = 0; i < verxs; i++) {
graph.data[i] = data[i];
for ( j = 0; j < verxs; j++) {
graph.weight[i][j] = weight[i][j];
}
}
}
//显示图的方法
public void showGraph(MGraph graph){
for (int[] link:graph.weight ) {
System.out.println(Arrays.toString(link));
}
}
//编写prim算法,得到最小生成树
/**
*
* @param graph 图
* @param v 表示从图的第几个顶点开始生成‘A'->0,'B'->1
* */
public void prim(MGraph graph, int v){
//标记顶点是否被访问过
int visited[] = new int[graph.verxs];
//visited[]默认元素的值都是0,表示没有访问过
// for (int i = 0; i < graph.verxs; i++) {
// visited[i] = 0;
// }
//把当前节点标记为已访问
visited[v] = 1;
//h1,h2记录两个顶点的坐标
int h1 = -1;
int h2 = -1;
int minWeight = 10000;//初始 成一个大值,后面在遍历过程中,会被替换
for (int k = 1; k < graph.verxs; k++) {//因为有graph.verxs个顶点,普利姆算法结束后,有graph.verxs-1边
//确定每一次生成的子图,和哪个节点的距离最近
for (int i = 0; i < graph.verxs; i++) {//i表示被访问过的节点
for (int j = 0; j < graph.verxs; j++) {//j表示还没访问过的节点
if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight){
//替换minWeight(寻找被访问过的节点和还没访问过的节点间权值最小的边)
minWeight = graph.weight[i][j];
h1=i;
h2 = j;
}
}
}
//找到一条边是最小
System.out.println("边<"+graph.data[h1] + "," + graph.data[h2] + "> 权值:" + minWeight);
//将当前这个节点标记为已访问
visited[h2]=1;
// minWeight重新设置为1
minWeight = 10000;
}
}
}
class MGraph{
int verxs;//表示图的节点个数
char[] data;//存放节点数据
int[][] weight;//存放边,就是我们的邻接数据
public MGraph(int verxs){
this.verxs = verxs;
data = new char[verxs];
weight = new int[verxs][verxs];
}
}