贪心算法
思想:使用贪心算法进行求解时,也是将一个大问题分为若干小问题进行求解,在求解时,每一步都使用当前问题
的最优解,从而希望最终的结果也是最优的. 贪心算法得到的结果不一定是最优的结果,但都是与最优解接近的
结果
求解集合覆盖问题
问题描述:有若干个电台,每个电台覆盖若干个地区,找出覆盖所有地区的最少电台组合,如果使用穷举法,在电台
数量大时,组合的复杂度就是指数级,效率有限
代码实现
public class Greedy {
public static void main(String[] args) {
HashMap<String, HashSet<String>> radios = new HashMap<>();
HashSet<String> k1 = new HashSet<>();
HashSet<String> k2 = new HashSet<>();
HashSet<String> k3 = new HashSet<>();
HashSet<String> k4 = new HashSet<>();
HashSet<String> k5 = new HashSet<>();
k1.add("北京");
k1.add("上海");
k1.add("天津");
k2.add("广州");
k2.add("北京");
k2.add("深圳");
k3.add("成都");
k3.add("上海");
k3.add("杭州");
k4.add("上海");
k4.add("天津");
k5.add("杭州");
k5.add("大连");
radios.put("k1", k1);
radios.put("k2", k2);
radios.put("k3", k3);
radios.put("k4", k4);
radios.put("k5", k5);
List<String> results = new ArrayList<>();
Set<String> allAreas = new HashSet<>();
Set<String> strings = radios.keySet();
for (String string : strings) {
allAreas.addAll(radios.get(string));
}
Set<String> temp = new HashSet<>();
String maxKey;
int maxSetSize;
while (allAreas.size() > 0) {
maxKey = null;
maxSetSize = 0;
for (String key : strings) {
temp.clear();
Set<String> areas = radios.get(key);
temp.addAll(areas);
temp.retainAll(allAreas);
if (temp.size() > 0 && (maxKey == null || temp.size() > maxSetSize)) {
maxKey = key;
maxSetSize = temp.size();
}
}
allAreas.removeAll(radios.get(maxKey));
results.add(maxKey);
}
System.out.println(results);
}
}
Prim算法
基于贪心算法解决最小生成树问题
修路问题:
问题描述:有若干个村庄,已知任意两条村庄直接的路的长度,相同距离修路价格一定,求如何修路可使价格最少
并且所有的村庄都能互相到达
思路:将若干村庄可以看出一张网图,问题的解就是求该图的最小生成树,利用prim算法从一个顶点开始先找出该
点所有与之相连的顶点之间的边的最小权值的点将两点相连,再找与该两点相连的点的最小权值边再将此点与之
相连,循环此操作直到将所以的点都连上问题即可解答
代码实现
** 图类
public class PrimGraph {
int vertexes;
char[] data;
int[][] weight;
public PrimGraph(int vertexes) {
this.vertexes = vertexes;
data = new char[vertexes];
weight = new int[vertexes][vertexes];
}
}
** 最小生成树类
public class MinTree {
public void minTree(PrimGraph graph, char[] data, int[][] weight) {
graph.vertexes = data.length;
graph.data = data;
graph.weight = weight;
}
public void prim(PrimGraph graph, int startIndex) {
int minValue = 100;
int row = -1;
int column = -1;
int[] isVisited = new int[graph.vertexes];
isVisited[startIndex] = 1;
for (int k = 1; k < graph.vertexes; k++) {
for (int i = 0; i < graph.vertexes; i++) {
if (isVisited[i] == 1) {
for (int j = 0; j < graph.vertexes; j++) {
if (isVisited[j] == 0) {
if (graph.weight[i][j] < minValue) {
minValue = graph.weight[i][j];
row = i;
column = j;
}
}
}
}
}
System.out.println("连接顶点" + graph.data[row] + "和顶点" + graph.data[column] + " 权值为" + minValue);
isVisited[column] = 1;
minValue = 100;
}
}
}
** 测试类
public class PrimTest {
public static void main(String[] args) {
char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int vertexes = data.length;
int[][] weight = new int[][]{
{100, 5, 7, 100, 100, 100, 2},
{5, 100, 100, 9, 100, 100, 3},
{7, 100, 100, 100, 8, 100, 100},
{100, 9, 100, 100, 100, 7, 100},
{100, 100, 8, 100, 100, 5, 4},
{100, 100, 100, 4, 5, 100, 6},
{2, 3, 100, 100, 4, 6, 100}
};
MinTree minTree = new MinTree();
PrimGraph graph = new PrimGraph(vertexes);
minTree.minTree(graph, data, weight);
minTree.prim(graph, 0);
}
}
Kruskal算法
基于贪心算法解决最小生成树问题
与prim算法可以解决同样的问题,prim算法是基于顶点,每次选出最优的一点的边,kruskal是基于边,每次选出
所有边的最优边,基本思路是先将所有边依次从小到大排序进选择,把所有顶点看成一个森林,每次遍历查看是否
符合将边添加入森林的条件(就是判断是否与已知最小生成树内的边形成回路),循环此操作直到将所以顶点连起
来形成一棵树,这棵树便是该图的最小生成树
判断回路的思路是判断将要添加的边的两顶点在已知最小生成树中是否具有相同的顶点
代码实现
** 边的信息
public class EdgeInfo {
char start;
char end;
int weight;
public EdgeInfo(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "<"+start+","+end+","+weight+">";
}
}
** Kruskal类
public class Kruskal {
int edgeNum;
char[] vertex;
int[][] commons;
public static void main(String[] args) {
char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] commons = new int[][]{
{0, 12, 100, 100, 100, 16, 14},
{12, 0, 10, 100, 100, 7, 100},
{100, 10, 0, 3, 5, 6, 100},
{100, 100, 3, 0, 4, 7, 100},
{100, 100, 5, 4, 0, 2, 8},
{16, 7, 6, 100, 2, 0, 9},
{14, 100, 100, 100, 8, 9, 0}
};
Kruskal myKruskal = new Kruskal(vertex, commons);
myKruskal.kruskal();
}
public Kruskal(char[] vertex, int[][] commons) {
this.vertex = vertex;
this.commons = commons;
for (int i = 0; i < vertex.length; i++) {
for (int j = i + 1; j < vertex.length; j++) {
if (commons[i][j] != 100) {
edgeNum++;
}
}
}
}
public void sort(EdgeInfo[] edgeInfos) {
for (int i = 0; i < edgeInfos.length - 1; i++) {
for (int j = 0; j < edgeInfos.length - 1 - i; j++) {
if (edgeInfos[j].weight > edgeInfos[j + 1].weight) {
EdgeInfo temp = edgeInfos[j + 1];
edgeInfos[j + 1] = edgeInfos[j];
edgeInfos[j] = temp;
}
}
}
}
public int getVertexIndex(char info) {
for (int i = 0; i < vertex.length; i++) {
if (vertex[i] == info) {
return i;
}
}
return -1;
}
public EdgeInfo[] getEdgeInfos() {
int index = 0;
EdgeInfo[] e = new EdgeInfo[edgeNum];
for (int i = 0; i < commons.length; i++) {
for (int j = i + 1; j < commons[0].length; j++) {
if (commons[i][j] != 100) {
e[index] = new EdgeInfo(vertex[i], vertex[j], commons[i][j]);
index++;
}
}
}
return e;
}
public int getEnd(int[] ends, int i) {
while (ends[i] != 0) {
i = ends[i];
}
return i;
}
public void kruskal() {
int index = 0;
EdgeInfo[] res = new EdgeInfo[vertex.length - 1];
int[] ends = new int[vertex.length];
EdgeInfo[] edges = getEdgeInfos();
sort(edges);
for (int i = 0; i < edgeNum; i++) {
int start = getVertexIndex(edges[i].start);
int end = getVertexIndex(edges[i].end);
int startEndIndex = getEnd(ends, start);
int endEndIndex = getEnd(ends, end);
if (startEndIndex != endEndIndex) {
ends[startEndIndex] = endEndIndex;
res[index] = edges[i];
index++;
}
}
System.out.println("结果为 " + Arrays.toString(res));
}
}