美文网首页
克鲁斯卡尔Kruskal算法使用并查集+优先级队列实现

克鲁斯卡尔Kruskal算法使用并查集+优先级队列实现

作者: longLiveData | 来源:发表于2020-05-06 17:24 被阅读0次

kruskal算法

克鲁斯卡尔算法是一种用来寻找连通图中最小生成树的算法。

  • 连通图:在无向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该无向图为连通图。
  • 强连通图:在有向图中,若任意两个顶点vivi与vjvj都有路径相通,则称该有向图为强连通图。
  • 连通网:在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。
  • 生成树:一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。
  • 最小生成树:在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。

算法步骤及实现
1.将图的所有连接线去掉,只剩顶点
2.从图的边集数组中找到权值最小的边,将边的两个顶点连接起来
3.继续寻找权值最小的边,将两个顶点之间连接起来,如果选择的边使得最小生成树出现了环路,则放弃该边,选择权值次小的边
4.直到所有的顶点都被连接在一起并且没有环路,最小生成树就生成了。
在步骤2中,可以用优先级队列获取当前权值最小的边
在步骤3中,可以使用并查集判断当前是否出现了环路

实现代码

import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Set;

public class Kruskal {

    // Union-Find Set
    // 实现并查集,用来判断是否出现环路
    public static class UnionFind {
        private HashMap<Node, Node> fatherMap;
        private HashMap<Node, Integer> rankMap;

        public UnionFind() {
            fatherMap = new HashMap<Node, Node>();
            rankMap = new HashMap<Node, Integer>();
        }

        private Node findFather(Node n) {
            Node father = fatherMap.get(n);
            if (father != n) {
                father = findFather(father);
            }
            fatherMap.put(n, father);
            return father;
        }

        public void makeSets(Collection<Node> nodes) {
            fatherMap.clear();
            rankMap.clear();
            for (Node node : nodes) {
                fatherMap.put(node, node);
                rankMap.put(node, 1);
            }
        }

        public boolean isSameSet(Node a, Node b) {
            return findFather(a) == findFather(b);
        }

        public void union(Node a, Node b) {
            if (a == null || b == null) {
                return;
            }
            Node aFather = findFather(a);
            Node bFather = findFather(b);
            if (aFather != bFather) {
                int aFrank = rankMap.get(aFather);
                int bFrank = rankMap.get(bFather);
                if (aFrank <= bFrank) {
                    fatherMap.put(aFather, bFather);
                    rankMap.put(bFather, aFrank + bFrank);
                } else {
                    fatherMap.put(bFather, aFather);
                    rankMap.put(aFather, aFrank + bFrank);
                }
            }
        }
    }

    // 自定义比较器实现小根堆
    public static class EdgeComparator implements Comparator<Edge> {
        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight - o2.weight;
        }
    }

    // 实现克鲁斯卡尔算法
    public static Set<Edge> kruskalMST(Graph graph) {
        UnionFind unionFind = new UnionFind();
        unionFind.makeSets(graph.nodes.values());
        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator()); // 按照边的权值组成小根堆
        for (Edge edge : graph.edges) {
            priorityQueue.add(edge); // 遍历所有的边都加入队列
        }
        Set<Edge> result = new HashSet<>();
        while (!priorityQueue.isEmpty()) {
            Edge edge = priorityQueue.poll(); // 按权值依次加入一条边
            if (!unionFind.isSameSet(edge.from, edge.to)) { // 如果不在同一个集合说明没有构成回路,选中这条边
                result.add(edge);
                unionFind.union(edge.from, edge.to);//选中这条边之后,把这条边连接的两个节点合在一起当成一个整体
            }
        }
        return result;
    }
}

相关文章

网友评论

      本文标题:克鲁斯卡尔Kruskal算法使用并查集+优先级队列实现

      本文链接:https://www.haomeiwen.com/subject/xdvwghtx.html