最小生成树:最小权重生成树的简称。在一给定的加权无向图G=(V,E)中,(u,v)代表连接顶点u和顶点v的便,而w(u,v)代表此边的权重。
若存在T中的顶点是所有V,T的边是E的子集中,且T中没有环,而w(T)最小,则此T为G的最小生成树。
普里姆算法(Prim’s algorithm),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点,且其所有边的权值之和亦为最小。
算法原理
“加点法”, 每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
Step 01:以某一个点开始,寻找当前该点可以访问的所有的边;
Step 02:在寻找的边中寻找最小的边,这个边必须由一个点还没有访问过, 将还没有访问的点加入我们的集合,记录添加的边;
Step 03:寻找当前集合可以访问的所有边,重复
Step 02
,直到没有新的点可以加入;Step 04: 此时由所有边构成的树即为最小生成树。
代码
package Graph;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Prim {
public static List vertexList = new ArrayList();//结点集
public static List EdgeQueue = new ArrayList();//边集
public static List newVertex = new ArrayList();//已经 访问过的结点
public static void main(String[] args) {
primTree();
}
public static void buildGraph() {
Vertex v1 = new Vertex("a");
Prim.vertexList.add(v1);
Vertex v2 = new Vertex("b");
Prim.vertexList.add(v2);
Vertex v3 = new Vertex("c");
Prim.vertexList.add(v3);
Vertex v4 = new Vertex("d");
Prim.vertexList.add(v4);
Vertex v5 = new Vertex("e");
Prim.vertexList.add(v5);
addEdge(v1, v2, 6);
addEdge(v1, v3, 7);
addEdge(v2, v3, 8);
addEdge(v2, v5, 4);
addEdge(v2, v4, 5);
addEdge(v3, v4, 3);
addEdge(v3, v5, 9);
addEdge(v5, v4, 7);
addEdge(v5, v1, 2);
addEdge(v4, v2, 2);
}
public static void addEdge(Vertex a, Vertex b, int w) {
Edge e = new Edge(a, b, w);
Prim.EdgeQueue.add(e);
}
public static void primTree() {
buildGraph();
Vertex start = vertexList.get(0);
newVertex.add(start);
for (int n = 0; n < vertexList.size() - 1; n++) {
Vertex temp = new Vertex(start.key);
Edge tempedge = new Edge(start, start, 1000);
for (Vertex v : newVertex) {
for (Edge e : EdgeQueue) {
if (e.start == v && !containVertex(e.end)) {
if (e.key < tempedge.key) {
temp = e.end;
tempedge = e;
}
}
}
}
newVertex.add(temp);
}
Iterator it = newVertex.iterator();
while (it.hasNext()) {
Vertex v = (Vertex) it.next();
System.out.println(v.key);
}
}
private static boolean containVertex(Vertex vte) {
for (Vertex v : newVertex) {
if (v.key.equals(vte.key))
return true;
}
return false;
}
//结点结构
private static class Vertex {
String key;
Vertex(String key){
this.key = key;
}
}
//边结构
private static class Edge {
Vertex start;
Vertex end;
int key;
public Edge(Vertex start, Vertex end, int key) {
this.start = start;
this.end = end;
this.key = key;
}
}
}
克鲁斯克尔算法(Kruskal)是一种查找最小生成树的算法,由Joseph Kruskal在1956年发表。解决同类问题的还有Prim算法和Boruvka算法,都是贪心算法的应用。
按照边的权重顺序(从小到大)将边加入生成树中,但是若加入该边会与生成树形成环则不加入该边。直到树中含有V-1条边为止。这些边组成的就是该图的最小生成树。
算法原理
“加边法”,初始化最小生成树边数为0,每迭代一次就选择一条满足条件的最小边为代价,加入到最小生成树的边集合里。
Step 01:把图中最所有边按代价从小到大排序;
Step 02:把图中的n个顶点堪称独立的n棵树组成的森林;
Step 03:按权值从小到大选择边,所选的边连接的两个顶点u和v,应该属于两颗不同的树,则成为最小生成树的一条边,并将两棵树合并为一棵树。
Step 04:重复
Step 03
,直到所有顶点都在一棵树内或者有n-1条边为止。
Boruvka 算法的演示如下(引自维基zh.wikipedia.org):
Brouvka算法又名Sollin算法。是最小生成树最古老的一个算法之一,其实是Prim算法和Kruskal算法的综合,每次迭代同时扩展多课子树,直到得到最小生成树T。适用于并行处理一个图。
“从所有当前的连通块向其他连通块扩展出最小边,直到只剩一个连通块”,其中取最小边的贪心思想是 Kruskal 的主体,而向外扩展又是 Prim 的思想 —— 基于另外两种生成树算法,Boruvka 的正确性显然。
复杂度:最坏O((n+m)logn)O((n+m)logn),随机图O(n+m)O(n+m)。