Kruskal算法实现最小生成树
其实算法理解起来比较简单:
1 取出所有的边,对边进行排序(升序)
2 判断边是否合格
2-1判断边上的两个点是否来自于同一个集合(需要用到并查集)
3 将合格的边加到最小生成树边的集合中
4 输出这个集合
并查集的实现
并查集其实就两个功能:
1 find:找某个点的父结点
2 union:合并两个点为一个集合(使其中一个点成为另一个点的父结点)
import java.util.HashSet;
import java.util.Set;
public class UnionFind {
public static UFNode find(UFNode x)
{
UFNode p=x;
Set<UFNode> path=new HashSet<>();
//记录向上追溯的路径上的点
while(p.parent!=null) {
path.add(p);
p=p.parent;
}
//这些点的parent全部指向这个集的代表(他们共同的老大)
for(UFNode ppp:path)
{
ppp.parent=p;
}
return p;
/*
* 问题:
* 如果每次查找都要从"叶子"一直找到"根",很费时
* 解决:
* 因为这个集合的形态对结果并无影响
* 所以只要做查找的时候,把找的结点都存在一个集合中
* 然后将他们的父结点统一设置为共同的老大
* 形象点说就是将一条长链掰圆
*
* 这样做就省去了很多查询时间
* */
}
public static void union(UFNode x,UFNode y)
{
find(y).parent=find(x);
//将x作为y的父结点
}
//定义静态内部类,这是并查集中的结点
public static class UFNode{
UFNode parent;//父结点
}
}
Kruskal实现代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Kruskal {
private final List<Edge> edgeList;//存放图中的所有边
private final int n;//总顶点数
private Set<Edge> T =new HashSet<>();//存放生成树的边
private Map pntAndNode=new HashMap();//边上的每个顶点都和并查集中有与之对应的node
private Set<Edge> getT(){
buildMST();
return T;
}
public Kruskal(List<Edge> edgeList,int n)
{
this.edgeList=edgeList;
//为每个顶点建立一个并查集的点
for(Edge edge:edgeList)
{
pntAndNode.put(edge.getStart(), new UnionFind.UFNode());
pntAndNode.put(edge.getEnd(), new UnionFind.UFNode());
}
this.n=n;
}
//构造一个边表
private static List<Edge> build()
{
List<Edge> li=new ArrayList<>();
li.add(new Edge("C","D",1));
li.add(new Edge("C","A",1));
li.add(new Edge("C","E",8));
li.add(new Edge("A","B",3));
li.add(new Edge("D","E",3));
li.add(new Edge("B","C",5));
li.add(new Edge("B","E",6));
li.add(new Edge("B","D",7));
li.add(new Edge("A","D",2));
li.add(new Edge("A","E",9));
return li;
}
//最小生成树的核心代码
private void buildMST() {
//先对边集进行排序
Collections.sort(edgeList);
for(Edge e:edgeList)
{
//寻找每条边上两个结点在map集合中映射的UFNode
UnionFind.UFNode x=(UnionFind.UFNode) pntAndNode.get(e.getStart());
UnionFind.UFNode y=(UnionFind.UFNode) pntAndNode.get(e.getEnd());
if(UnionFind.find(x)==UnionFind.find(y))
continue;//如果两个结点来自同一顶点集,则跳过这条边,否则会形成回路
UnionFind.union(x, y);
//把边加入到T中
T.add(e);
if(T.size()==n-1)
return;//生成树的边数==总顶点数-1,表示所有的点已经连接
}
/*
* 整体思路
* 对边进行排序
* 判断边是否合格
* 判断边上的两个点是否来自于同一顶点集
* 合格就加到生成树的边集中
*
* */
}
public static void main(String[] args) {
List<Edge> edgeList=build();
Kruskal obj=new Kruskal(edgeList,5);
for(Edge e:obj.getT())
System.out.println(e);
//getT中就调用了buildMST方法,将生成的边放到了集合中
}
}
Kruskal代码涉及到的Edge类代码
public class Edge<T> implements Comparable<Edge> {
private T start;
private T end;
private int distance;
/**
* @param start
* @param end
* @param distance
*/
public Edge(T start, T end, int distance) {
this.start = start;
this.end = end;
this.distance = distance;
}
public T getStart() {
return start;
}
public void setStart(T start) {
this.start = start;
}
public T getEnd() {
return end;
}
public void setEnd(T end) {
this.end = end;
}
public int getDistance() {
return distance;
}
public void setDistance(int distance) {
this.distance = distance;
}
@Override
public String toString() {
return start+"->"+end+":"+distance;
}
@Override
public int compareTo(Edge obj) {
int targetDis=obj.getDistance();
return distance>targetDis?1:(distance==targetDis?0:-1);
}
}