求最小生成树,最经典的算法无非是prim算法和kruskal算法,而时间效率上,kruskal更胜一筹。对于kruskal的实现,这里我们采用并查集的思想。
很简单,也就是说,我们先对边按权值从小到大排序,然后遍历每一条边。
遍历过程中的每一条边,我们判断边的两个节点是否是连通的:
(1)若连通,则跳过此边;
(2)若不连通,则此边一定为最小生成树上的一条边,将此边加入结果集;
当遍历完所有的边,结果集里的所有边即组成了这个无向图的最小生成树。
并查集的作用就是来判断边上的两点是否连通,对于并查集,如不了解,建议先学习其相关知识,非常简单,这里不做讲解。
下面附上代码,代码关键的地方带有注释,如不理解请留言。
package test;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
public class MinSpanningTree {
class Edge {//内部类定义边的数据结构
int u, v, weight;
}
ArrayList<Edge> Edges = new ArrayList<Edge>();
Map<Integer, Integer> nodeFather = new HashMap<Integer, Integer>();
int cnt = 0, nodeCnt = 0;
public MinSpanningTree(String path) {
try {
BufferedReader br = new BufferedReader(new FileReader(path));
String str;
String[] strArray;
while ((str = br.readLine()) != null) {
strArray = str.split("\\s");
Edges.add(cnt, new Edge());
Edges.get(cnt).u = Integer.parseInt(strArray[0]);
Edges.get(cnt).v = Integer.parseInt(strArray[1]);
Edges.get(cnt).weight = Integer.parseInt(strArray[2]);
if (!nodeFather.containsKey(Edges.get(cnt).u)) {
nodeFather.put(Edges.get(cnt).u, Edges.get(cnt).u);//初始化,father[i]=i;
++nodeCnt;
}
if (!nodeFather.containsKey(Edges.get(cnt).v)) {
nodeFather.put(Edges.get(cnt).v, Edges.get(cnt).v);
++nodeCnt;
}
++cnt;
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean union(int u, int v) {//并操作
int a = find(u);
int b = find(v);
if (a != b) {
nodeFather.put(a, b);
return true;
}
return false;
}
public int find(int x) {//查操作
if (x != nodeFather.get(x)) {
nodeFather.put(x, find(nodeFather.get(x)));
}
return nodeFather.get(x);
}
public ArrayList<Edge> getMinSpanningTree() {
ArrayList<Edge> result = new ArrayList<Edge>();
Queue<Edge> FsQueue = new PriorityQueue<Edge>(1000,//设置优先队列,使按边权值从小到大排序
new Comparator<Edge>() {
public int compare(Edge EdgeOne, Edge EdgeTwo) {
if (EdgeOne.weight > EdgeTwo.weight)
return 1;
else if (EdgeOne.weight < EdgeTwo.weight)
return -1;
else
return 0;
}
});
for (int i = 0; i < cnt; ++i) {
FsQueue.add(Edges.get(i));
}
while (!FsQueue.isEmpty()) {//遍历每一条边
Edge Edge = FsQueue.poll();
if (union(Edge.u, Edge.v)) {
result.add(Edge);
} else {
continue;
}
}
return result;
}
public static void main(String[] args) {
MinSpanningTree mstree = new MinSpanningTree("c:/treedata.txt");
ArrayList<Edge> result = mstree.getMinSpanningTree();
for (int i = 0; i < result.size(); ++i) {
System.out.println(result.get(i).u + " " + result.get(i).v + " "+ result.get(i).weight);
}
}
}
输入文件的格式为:
u v weight,即输入1 2 5,代表一条边,边的顶点是1和2,1和2之间的权值是5
输出格式亦是
u v weight,包含了最小生成树的所有边