Skip to content

Commit 2fd0d4a

Browse files
committed
KrusKal算法
1 parent c503d6a commit 2fd0d4a

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.zejian.structures.Graph.WeightGraph;
2+
3+
import com.zejian.structures.heap.MinHeap;
4+
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
/**
9+
* Created by zejian on 2018/1/30.
10+
* Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
11+
* 利用Kruskal算法求最小生成树(只适合无向图)
12+
* 算法思想:按照边的权重排序(最小堆数据结构),每次将最小权重边加入最小生成树(List数据结构存储就行)
13+
* 但在加入前要判断加入的边是否会与已存在的生成树中的边形成环,如果形成环,那么就丢弃该边,不加入最小
14+
* 生成树,以次循环直到最小生成树含有V-1条边,那么就结束操作,此时List中存储的边就是一棵最小生成树.
15+
*
16+
* 需要的工具:最小堆数据结构(排序所有边)+List(存储最小生成树)+并查集UF(用于判断是否形成环)
17+
*/
18+
public class KruskalMST<Weight extends Number & Comparable<Weight>> {
19+
20+
private List<Edge<Weight>> mst;//最小生成树所包含的所有边
21+
private Number mstWeight; //最小生成树的总权值
22+
private int count = 0;
23+
public KruskalMST(WeightGraph graph){
24+
assert graph != null;
25+
mst = new ArrayList<Edge<Weight>>();
26+
27+
MinHeap<Edge<Weight>> pq = new MinHeap<Edge<Weight>>(graph.E());
28+
//遍历所有边并加入pq
29+
for (int i = 0; i <graph.V() ; i++) {
30+
for (Object item: graph.adj(i)) {
31+
if(item != null) {
32+
Edge<Weight> e = (Edge<Weight>) item;
33+
//因为是无向图,存在重复边.所以这里需要判断一下如(0,1)和(1,0)是同一条边
34+
if (e.v() < e.w()) {
35+
count ++;
36+
System.out.println("count:"+count+",e="+e.toString());
37+
pq.insert(e);
38+
}
39+
}
40+
}
41+
}
42+
//创建并查集辅助类,用于判断是否存在环
43+
UnionFind uf = new UnionFind(graph.V());
44+
45+
//最小生成树含有V-1条边时停止
46+
while (!pq.isEmpty()&& mst.size() < graph.V() - 1 ){
47+
//取出最小权值值边
48+
Edge<Weight> e = pq.deleteMin();
49+
50+
int v = e.either();
51+
int w = e.other(v);
52+
//判断要相连的两个顶点是否有相同根结点,没有就可以加入生成树
53+
//如果有那么说明相连后肯定形成环,抛弃该边
54+
if(uf.isConnected(v,w)){
55+
continue;
56+
}
57+
58+
mst.add(e);
59+
//把已访问过的最小生成树的边结点加入并查集
60+
uf.unionElements(v,w);
61+
}
62+
63+
//计算最小生成树的总权值
64+
mstWeight = mst.get(0).wt().doubleValue();
65+
for (int i = 1; i < mst.size() ; i++) {
66+
mstWeight =mstWeight.doubleValue() + mst.get(0).wt().doubleValue();
67+
}
68+
}
69+
70+
// 返回最小生成树的所有边
71+
List<Edge<Weight>> getMstEdgeList(){
72+
return mst;
73+
}
74+
75+
// 返回最小生成树的权值
76+
Number mstWeight(){
77+
return mstWeight;
78+
}
79+
80+
81+
/**
82+
* TODO:测试未通过.............
83+
* @param args
84+
*/
85+
public static void main(String[] args) {
86+
87+
String filename = "testWG1.txt";
88+
WeightSparseGraph<Double> spare = new WeightSparseGraph<Double>(8,false);
89+
spare.readGraph(filename);
90+
spare.show();
91+
92+
KruskalMST<Double> kMST = new KruskalMST<Double>(spare);
93+
List<Edge<Double>> mstList = kMST.getMstEdgeList();
94+
for( int i = 0 ; i < mstList.size() ; i ++ )
95+
System.out.println(mstList.get(i));
96+
System.out.println("总权值为:"+kMST.mstWeight());
97+
98+
99+
}
100+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.zejian.structures.Graph.WeightGraph;
2+
3+
/**
4+
* Created by zejian on 2018/1/30.
5+
* Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
6+
* 并查集,路径压缩 UF
7+
* 主要用于判断是否形成环
8+
*/
9+
public class UnionFind {
10+
11+
private int rank[]; // rank[i]表示以i为根的集合所表示的树的层数
12+
private int parent[];// parent[i]表示第i个元素所指向的父节点
13+
private int count; // 数据个数
14+
15+
16+
public UnionFind(int count){
17+
assert count > 0;
18+
this.count = count;
19+
rank = new int[count];
20+
parent = new int[count];
21+
//初始化所有结点的父结点都指向自己;
22+
for (int i = 0; i <count ; i++) {
23+
parent[i] = i;
24+
rank[i] = 1;
25+
}
26+
}
27+
28+
/**
29+
* 查找某个结点的根结点
30+
* 时间复杂父为O(h) h为树的高度
31+
* @param p
32+
* @return
33+
*/
34+
private int findRoot(int p){
35+
assert( p >= 0 && p < count );
36+
while(p != parent[p]) {
37+
//尝试进行路径压缩
38+
//把p的父结点修改为parent[p]的父结点,从而进行路径压缩
39+
parent[p] = parent[parent[p]];
40+
//p 赋值为 现在的父结点parent[p],进行下一轮循环
41+
p = parent[p];
42+
}
43+
return p;
44+
}
45+
46+
/**
47+
* 判断两个结点是否相连
48+
* 或者查看元素p和元素q是否所属一个集合
49+
* O(h)复杂度, h为树的高度
50+
* @param p
51+
* @param q
52+
* @return
53+
*/
54+
public boolean isConnected(int p , int q){
55+
return findRoot(p) == findRoot(q);
56+
}
57+
58+
59+
60+
/**
61+
* 合并元素p和元素q所属的集合, O(h)复杂度, h为树的高度
62+
* @param p
63+
* @param q
64+
*/
65+
public void unionElements(int p, int q){
66+
67+
int pRoot = findRoot(p);
68+
int qRoot = findRoot(q);
69+
//如果根结点相同说明已关联
70+
if (pRoot == qRoot) return;
71+
72+
//如果pRoot的树层数小于qRoot层数,为了路径更短,将pRoot挂到qRoot树下
73+
if(rank[pRoot] < rank[qRoot]){
74+
parent[pRoot] = qRoot;
75+
}else if(rank[pRoot] > rank[qRoot]){
76+
parent[qRoot] = pRoot;
77+
}else {
78+
// rank[pRoot] == rank[qRoot]
79+
//如果根结点层数相同,随机选择一个并入即可但要更新rank
80+
parent[qRoot] = pRoot;
81+
rank[qRoot]++;
82+
}
83+
}
84+
85+
}

0 commit comments

Comments
 (0)