基于并查集的Kruskal算法求最小生成树

不了解并查集的可以看我之前写的: 👇https://blog.csdn.net/Smartbbbb/article/details/123623055?spm=1001.2014.3001.5501 


/**
 * 最小生成树(保持树的连通性,保证权值最小)
 *
 * K算法:
 *      思路: 将所有边按照权值排序,从最低权值的边开始,判断边两边的节点
 *      是否在同一集合,不在union,在,放弃这条边 ... 直到遍历结束,使用
 *      并查集解决,最终连成一片,因为并查集解决的就是连通性的问题!
 *  
 *      使用并查集处理时核心方法是getParent,它是判定是否为同集合的关键
 *      有些节点的父节点并不一样但是同属于一个集合,所以需要往上迭代,所以
 *      又做了扁平化处理(使用栈)
 *      
 *      数据结构选择: HashMap,Set,PriorityQueue
 *
 *      注: 对于无向图时,那就少一条边,只要边两边节点已经是一个集合,那么就不要了
 */
public class Kruskal {
    public static void main(String[] args) {
        int[][] matrix = {
                {5, 1, 5},
                {10, 4, 1},
                {12, 6, 1},
                {7, 6, 5},
                {8, 8, 6},
                {1, 5, 8},
                {40, 8, 4},
                {3, 4, 6}
        };
        Graph graph = GraphGenerate.generate(matrix);
        Set<Edge> edges = kruskal(graph);
        System.out.println(edges);
    }


    //求最小生成树,返回结果为最小权值边集合
    public static Set<Edge> kruskal(Graph graph){
        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
        priorityQueue.addAll(graph.edges);
        UnionSet unionSet = new UnionSet();
        unionSet.initMap(graph.nodes.values());//初始化

        Set<Edge> result = new HashSet<>();
        while (!priorityQueue.isEmpty()){
            Edge cur = priorityQueue.poll();
            Node from = cur.from;
            Node to = cur.to;
            if(!unionSet.isSameSet(from,to)){
                unionSet.union(from,to);
                result.add(cur);
            }
        }
        return result;
    }

    private static class EdgeComparator implements Comparator<Edge>{
        @Override
        public int compare(Edge o1, Edge o2) {
            return o1.weight - o2.weight;
        }
    }

    private static class UnionSet{
        public HashMap<Node,Node> parents;
        public HashMap<Node,Integer> sizeMap;

        public UnionSet() {
            this.parents = new HashMap<>();
            this.sizeMap = new HashMap<>();
        }
        
        public void initMap(Collection<Node> nodes){
            this.parents.clear();
            this.sizeMap.clear();
            for (Node node : nodes) {
                this.parents.put(node,node);
                this.sizeMap.put(node,1);
            }
        }
        
        //判断是否属于同一集合
        public boolean isSameSet(Node left,Node right){
            if(left == null || right == null){
                return false;
            }
            return getFather(left) == getFather(right);
        }
        
        //合并不同集合的元素
        public void union(Node left,Node right){
            if(left == null || right == null){
                return;
            }
            Node lf = getFather(left);
            Node rf = getFather(right);
            if(lf != null && rf != null){
                if(lf != rf){
                    int lsize = sizeMap.get(lf);
                    int rsize = sizeMap.get(rf);

                    Node big = lsize < rsize ? rf : lf;
                    Node small = big == rf ? lf : rf;
                    parents.put(small,big);
                    sizeMap.put(big,lsize + rsize);
                    sizeMap.remove(small);
                }
            }

        }

        //获取节点所在集合的集合代表
        private Node getFather(Node node){
            if(node == null){
                return null;
            }
            Stack<Node> stack = new Stack<>();
            Node cur = node;
            while (cur != parents.get(cur)){
                stack.push(cur);
                cur = parents.get(cur);
            }
            //扁平化处理形成的并查集树
            while (!stack.isEmpty()){
                parents.put(stack.pop(),cur);
            }
            return cur;
        }
    }
}

 左肾算法学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值