面对的问题:
- 有7个村庄(A,B,C,D,E,EG),现在需要修路把7个村庄连通
- 各个村庄的距离用边线表示(权),比如A-B距离5公里
- 问:如何修路保证各个村庄都能连通,并且总的修建公路总里程最短?
思路:将10条边,连接即可,但是总的里程数不是最小.
正确的思路,就是尽可能的选择少的路线,并且每条路线最小,保证总里程数最少
最小生成树:
修路问题本质就是就是最小生成树问题,先介绍一 下最 小生成树(Minimum Cost Spanning Tree,简称MST.
- 给定一个带权的无向连通图,如何选取一棵生成树,使树 上所有边上权的总和为最小,这叫最小生成树
- N个顶点,一定有N-1条边
- 包含全部顶点
- N-1条边都在图中
- 求最小生成树的算法主要是普里姆算法和克鲁斯卡尔算法用,
普里姆算法介绍:
普利姆(Prim)算法求最小生成树, 也就是在包含n个顶点的连通图中,找出只有(n-1)条边包含 所有n个顶点的连通子图,也就是所谓的极小连通子图。
普利姆的算法如下:
- 设G=(V,E)是连通网, T=(U,D)是最小生 成树,V,U是顶点集合,E,D是边的集合
- 若从顶点u开始构造最小生成树,则从集合v中取出顶点u放入集合U中,标记项点v的visited[u]=1
- 若集合U中顶点ui与集合V-U中的项点vj之间存在边,则寻找这些边中权值最小的边,但不能构成回路,将顶点vj加入集合U中,将边(ui,vj) 加入集合D中,标记visited[vi]=1
- 重复步骤2,直到U与V相等,即所有项点都被标记为访问过,此时D中有n-1条边
图解:
代码实现:
package prim;
import java.util.Arrays;
public class PrimAlgorithm {
public static void main(String[] args) {
char[] data = new char[] { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
int n = data.length;
int[][] weight = new int[][] { { 10000, 5, 7, 10000, 10000, 10000, 2 }, { 5, 10000, 10000, 9, 10000, 10000, 3 },
{ 7, 10000, 10000, 10000, 8, 10000, 10000 }, { 10000, 9, 10000, 10000, 10000, 4, 10000 },
{ 10000, 10000, 8, 10000, 10000, 5, 4 }, { 10000, 10000, 10000, 4, 5, 10000, 6 },
{ 2, 3, 10000, 10000, 4, 6, 10000 }, };
Graph gp = new Graph(n);
minTree tree = new minTree();
tree.create(gp, n, data, weight);
tree.show(gp);
tree.prim(gp, 0);
}
}
class minTree {
// 创建图的邻接矩阵
public void create(Graph gp, int n, char[] data, int[][] weight) {
int i, j;
for (i = 0; i < n; i++) {
gp.data[i] = data[i];
for (j = 0; j < n; j++) {
gp.weight[i][j] = weight[i][j];
}
}
}
public void show(Graph gp) {
for (int[] link : gp.weight) {
System.out.println(Arrays.toString(link));
}
}
// v:从哪个顶点开始
public void prim(Graph gp, int v) {
// 判断节点是否被访问,默认为0
int[] visited = new int[gp.vertexs];
for (int i = 0; i < visited.length; i++) {
visited[i] = 0;
}
// 把当前这个节点标志已访问
visited[v] = 1;
// 用其记录俩个顶点的下标
int h1 = -1;
int h2 = -1;
int minWeight = 10000;
for (int k = 1; k < gp.vertexs; k++) {
// 确定每一次生成的子图,和哪个节点的距离最小
for (int i = 0; i < gp.vertexs; i++) {
for (int j = 0; j < gp.vertexs; j++) {
if (visited[i] == 1 && visited[j] == 0 && gp.weight[i][j] < minWeight) {
minWeight = gp.weight[i][j];
h1 = i;
h2 = j;
}
}
}
System.out.println("边 = < " + gp.data[h1] + " , " + gp.data[h2] + " >, " + minWeight);
visited[h2] = 1;
minWeight=10000;
}
}
}
class Graph {
int vertexs;
char[] data;
int[][] weight;
public Graph(int n) {
this.vertexs = n;
data = new char[n];
weight = new int[n][n];
}
}
运行结果: