算法和数据结构-最小生成树之Kruskal

1. 最小生成树

 什么是最小生成树?
    1. 图中每个节点要连通
    2. 在保证1的前提下边的权重和最小
 Kruskal:最小生成树算法之一(从边考虑),要求图为无向图,算法思想:
    1. 将图中的边全部移除,只剩点
    2. 找到当前权重最小的边添加到图中,检查是否形成环
    3. 没有形成环说明可以添加到图中,继续找下一个小边;形成了就不添加到图中,继续找下一条小边
    4. 重复2-3直到全部节点连通

2.检查环

 如何检查是否形成环?并查集可以实现,这里使用自定义的效率较低。假设图中的节点集为{A,B,C,D}
    1. 每个节点形成一个set集合,即{A}{B}{C}{D}
    2. 找到权重最小的边,看其from节点和to节点(假如分别为AB)
    3.{A}{B}合并为一个集合{A,B}
    4. 找下一个最小的边,看其from节点和to节点(假如分别为BC)
    5.{A,B}{C}合并为一个集合{A,B,C}
    6. 找下一个最小的边,看其from节点和to节点(假如分别为AC)
    7. 由于AC已经在一个集合中,说明添加该边后就会形成环
    8. 继续找下一个边直至所有节点都在集合中

3. 代码实现

public class Kruskal {

    public static class MySets{
        // 存储节点和其所在集合
        public HashMap<Node, List<Node>> setMap;

        // 每个节点形成一个set集合
        public MySets(List<Node> nodes){
            for (Node cur : nodes){
                List<Node> set = new ArrayList<>();
                set.add(cur);
                setMap.put(cur, set);
            }
        }

        // 判断from节点和to节点所在集合是否为同一个集合(通过地址判断)
        public boolean isSameSet(Node from, Node to){
            List<Node> fromSet = setMap.get(from);
            List<Node> toSet = setMap.get(to);
            return fromSet == toSet;
        }

        // 将from节点和to节点所在集合合并为一个集合
        public void union(Node from, Node to){
            List<Node> fromSet = setMap.get(from);
            List<Node> toSet = setMap.get(to);

            // 将to节点的集合合并到from节点的集合中,并将to节点集合中所有节点全部映射到from节点集合中
            // 在后续from节点集合增加时,toNode对于的集合也会随之改变(地址引用)
            for (Node toNode : toSet){
                fromSet.add(toNode);
                setMap.put(toNode, fromSet);
            }
        }
    }

    // 自定义边的权重比较器
    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) {
        List<Node> values = (List<Node>) graph.nodes.values();
        // 1. 完成初始化(每个节点形成一个set集合)
        MySets mySets = new MySets(values);

        PriorityQueue<Edge> priorityQueue = new PriorityQueue<>(new EdgeComparator());
        // 2. 将图中的边按权重进行排序
        for (Edge edge : graph.edges){
            // 有M条边的话时间复杂度为O(logM)
            priorityQueue.add(edge);
        }

        Set<Edge> result = new HashSet<>();
        // 3. 找到当前权重最小的边添加到图中,检查是否形成环
        while (!priorityQueue.isEmpty()){
            Edge edge = priorityQueue.poll();  // 有M条边的话时间复杂度为O(logM)
            if (!mySets.isSameSet(edge.from, edge.to)){
                result.add(edge);
                mySets.union(edge.from, edge.to);
            }
        }
        return result;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值