最小生成树之Kruskal算法实现(基于并查集)

  Kruskal算法简单易懂的描述:
  假设有如下图:

在这里插入图片描述

  将上图按边的权值从小到大排序:

在这里插入图片描述

  按边的权值从小到大取边加入生成树,注意:最小生成树是不能有环的,所以每取一条边都要检查所取边的两个端点是否在之前取边时都取过,如果都取过则不能取该条边(需要继续往下检查看接下来的边能否取),如果只有一个端点曾经取过,那么这条边依然是可以加入生成树的。当取到的边总数等于顶点总数减1时,最小生成树就生成了。如图:

在这里插入图片描述

代码实现:

边集:
/**
 * 边集的封装
 * 边集可以用来表示图
 * @param <T> 泛型
 */
public class Edge<T>implements Comparable<Edge> {
    private  T start;
    private T end;
    private int 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);
    }
}
并查集:
import java.util.HashSet;
import java.util.Set;

/**
 * 并查集定义
 */
public class UnionFind {

    public static class UFNode {
        //父节点
        UFNode parent;
    }

    //查询
    public static UFNode find(UFNode x) {
        UFNode p = x;
        //用于先把不同深度的节点存起来,最后统一指向根结点,加快查询效率
        Set<UFNode> path = new HashSet<>();
        while (p.parent != null) {
            path.add(p);
            p = p.parent;
        }
        //将所有边指向同一个集合标识(根节点)
        for (UFNode ppp : path) {
            ppp.parent = p;
        }
        //返回集合标识(根节点)
        return p;
    }

    //合并
    public static void union(UFNode x, UFNode y) {
        //将两个不同的集合指向同一个集合标识(根节点)
        find(y).parent = find(x);
    }
}
Kruskal算法:
import java.util.*;

public class Kruskal {
    private final List<Edge> edgeList;
    //生成树的边集
    private Set<Edge> T = new HashSet<>();

    public Set<Edge> getT() {
        buildMST();
        return T;
    }

    private final int n;
    //存放每个顶点和并查集节点的映射关系
    private Map pntAndNode = new HashMap();

    public Kruskal(List<Edge> edgeList, int n) {
        this.edgeList = edgeList;
        this.n = n;
        //为每个顶点建立一个并查集节点
        for (Edge edge : edgeList) {
            pntAndNode.put(edge.getStart(), new UnionFind.UFNode());
            pntAndNode.put(edge.getEnd(), new UnionFind.UFNode());
        }
    }

    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);
        }
    }

    private static List<Edge> build() {
        List<Edge> l = new ArrayList<>();
        l.add(new Edge(("C"), "D", 1));
        l.add(new Edge(("C"), "A", 1));
        l.add(new Edge(("C"), "E", 2));
        l.add(new Edge(("A"), "B", 3));
        l.add(new Edge(("D"), "E", 3));
        l.add(new Edge(("B"), "C", 5));
        l.add(new Edge(("B"), "E", 6));
        l.add(new Edge(("B"), "D", 7));
        l.add(new Edge(("A"), "D", 2));
        l.add(new Edge(("A"), "E", 9));
        return l;
    }

    private void buildMST() {
        //排序
        Collections.sort(edgeList);
        //迭代
        for (Edge e : edgeList) {
            if (!ok(e)) {
                //该条边不能加入最小生成树的集合中
                continue;
            }
            //确认通过了,就把边加入最小生成树的集合中
            T.add(e);
            if (T.size() == n - 1) {
                //生成树的边数等于总顶点数-1,说明所有顶点都已连接
                return;
            }
        }
    }

    private boolean ok(Edge e) {
        //从map中找到边的起点对应的并查集节点
        UnionFind.UFNode x = (UnionFind.UFNode) pntAndNode.get(e.getStart());
        //从map中找到边的终点对应的并查集节点
        UnionFind.UFNode y = (UnionFind.UFNode) pntAndNode.get(e.getEnd());
        if (UnionFind.find(x) != UnionFind.find(y)) {
            UnionFind.union(x, y);
            return true;
        }
        return false;
    }
}
运行结果截图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值