概述
最小生成树 属于图论用语,它是连通图中具有最小边代价和的生成树。求取它的实际意义在于:如果需要为城镇部署线网,按照最小生成树进行部署,既可保证用户全覆盖,又可保证代价最小。
我们在此介绍两种最小生成树求解算法 —— Prim 算法 (又称 “加点法”) 和 Kruskal 算法 (又称 “加边法”)。
Prim 算法
Prim 算法步骤描述如下 (假定图中节点集合为 $V$):
- 选定图中某一节点为根节点,记为 $root$。
- 构建两个集合 $S$ 和 $T$ ($S$ 表示节点集合中属于最小生成树中节点的节点集,$T$ 表示节点集合中不属于最小生成树中节点的节点集),并初始化它们为 $S = {root}, T = V - {root}$。
- 选择 $T$ 中距离 $S$ 中节点最近的节点,将其从 $T$ 中删去并加入到 $S$ 中。
- 循环迭代步骤 3,直至 $S == V$。
- 从步骤描述中可以看到:需要时刻维护 $T$ 中节点到 $S$ 中节点的距离 (即边代价)。
- 最小生成树基于添加点而构建,故而其称为 “加点法”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public static int INF = 0x3fffffff;
public int[] prim(int[][] graph) { int[] destination = new int[graph.length]; int[] distance = new int[graph.length];
for (int i = 0; i < graph[0].length; i++) { destination[i] = 0; distance[i] = graph[0][i]; } distance[0] = 0;
for (int i = 1; i < graph.length; i++) { int minIndex = 0; int minDistance = INF;
for (int j = 0; j < distance.length; j++) { if (distance[j] != 0 && distance[j] < minDistance) { minIndex = j; minDistance = distance[j]; } }
distance[minIndex] = 0;
for (int j = 0; j < distance.length; j++) { if (distance[j] != 0 && distance[j] > graph[minIndex][j]) { destination[j] = minIndex; distance[j] = graph[minIndex][j]; } } }
return destination; }
|
Kruskal 算法
Kruskal 算法步骤描述如下 (假定图中节点集合为 $V$):
- 将图中所有边按照边代价从小到大进行排序。
- 清空图中所有边,使得图仅剩所有顶点。
- 从小到大依次选择边,如果该边对应的两个顶点已经连通,则不作任何处理,否则在图中添加此边。
- 循环迭代步骤 3, 直至图中添加地边数为 $V.size - 1$。
- 判断顶点是否连通,可使用并查集快速做到。
- 最小生成树基于添加边而构建,故而其称为 “加边法”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class EdgeInfo { int distance; int i; int j;
public EdgeInfo(int distance, int i, int j) { this.distance = distance; this.i = i; this.j = j; } }
public ArrayList<EdgeInfo> kruskal(int[][] graph) { UnionFind uf = new UnionFind(graph.length); ArrayList<EdgeInfo> result = new ArrayList<>(); PriorityQueue<EdgeInfo> edges = new PriorityQueue<>((o1, o2) -> { EdgeInfo edge1 = (EdgeInfo) o1; EdgeInfo edge2 = (EdgeInfo) o2; return (edge1.distance - edge2.distance); });
for (int i = 0; i < graph.length; i++) { for (int j = 0; j < graph[0].length; j++) { if (graph[i][j] != INF) { edges.add(new EdgeInfo(graph[i][j], i, j)); } } }
int k = 0; while (!edges.isEmpty()) { EdgeInfo tmp = edges.remove();
if (uf.find(tmp.i) != uf.find(tmp.j)) { uf.union(tmp.i, tmp.j); result.add(tmp); k++; }
if (k == graph.length - 1) { break; } }
return result; }
|