最小生成树-Prim 算法与 Kruskal 算法 Java 实现版

本文介绍了最小生成树的概念及其在连通无向图中的应用。详细阐述了Prim算法和Kruskal算法的原理,Prim算法通过逐步增加边来构造树,而Kruskal算法则按边的权重从小到大选择,避免形成环。同时,提供了BinaryHeap优先队列的源码链接,并邀请读者访问作者的Github仓库获取更多算法和数据结构相关资源。
摘要由CSDN通过智能技术生成

最小生成树

一个无向图 G 的最小生成树(minimum spanning tree) 就是由该图的那些连接 G 的所有顶点的边构成的树,且总价值最低。
最小生成树存在当且仅当 G 是连通的。

Prim 算法

Prim 算法是使其连续的一步步长成。在每一步,都要把一个节点当做跟并网上加边,这样也就把相关联的顶点增长到了树上。

/**
     * 类似 {@link Dijkstra} 算法。
     * 每次循环都选取 Table 中值最小的边。
     */
private static <T> void find(DGraph<T> graph, Vertex<T> vertex) {
    Map<Vertex<T>, TableEntity<Vertex<T>>> table = TableEntity.getTable(graph, vertex);
    DGraph<T> primGraph = new ListDGraph<>();
    for (int i = 0; i < graph.size(); i++) {
        Vertex<T> minVertex = findUnknownMin(table);
        if (minVertex == null) {
            break;
        }
        TableEntity<Vertex<T>> minTable = table.get(minVertex);
        minTable.know = true;
        primGraph.add(new Vertex<>(minVertex.getValue()));
        if (minTable.path != null) {
            T thisValue = minVertex.getValue();
            T pathValue = minTable.path.getValue();
            primGraph.add(new Edge<>(new Vertex<>(thisValue), new Vertex<>(pathValue)));
            primGraph.add(new Edge<>(new Vertex<>(pathValue), new Vertex<>(thisValue)));
        }
        if (minVertex.getEdgeList() != null) {
            for (Edge<Vertex<T>> edge : minVertex.getEdgeList()) {
                if (edge.getDest() != null) {
                    TableEntity<Vertex<T>> edgeTable = table.get(edge.getDest());
                    if (!edgeTable.know && edge.getWeight() < edgeTable.dist) {
                        edgeTable.dist = edge.getWeight();
                        edgeTable.path = minVertex;
                    }
                }
            }
        }
    }
    System.out.println();
    Utils.printGraph(primGraph);
}
/**
     * 从未知表中读取一个 dist 最小的顶点
     */
private static <T> Vertex<T> findUnknownMin(Map<Vertex<T>, TableEntity<Vertex<T>>> table) {
    int min = TableEntity.INFINITY;
    Vertex<T> vertex = null;
    for (Vertex<T> key : table.keySet()) {
        TableEntity<Vertex<T>> item = table.get(key);
        if (!item.know && min >= item.dist) {
            min = item.dist;
            vertex = key;
        }
    }
    return vertex;
}

Kruskal 算法

第二种贪婪策略是连续的按照最小的权选择边,并且当所选的边不产生圈时就把它当做取定的边。

/**
     * 1.新建图G,G中拥有原图中相同的节点,但没有边
     * 2.将原图中所有的边按权值从小到大排序
     * 3.从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中
     * 4.重复3,直至图G中所有的节点都在同一个连通分量中
     */
private static <T> DGraph<T> find(DGraph<T> graph, Vertex<T> vertex) {
    DGraph<T> newGraph = getNoEdgeGraph(graph);
    BinaryHeap<Edge<Vertex<T>>> priorityQueue = makePriorityQueue(graph);
    while (!priorityQueue.isEmpty()) {
        Edge<Vertex<T>> edge = priorityQueue.deleteMin();
        if (edge.getDest() != null
            && edge.getSource() != null) {
            if (!connected(newGraph, edge.getDest(), edge.getSource())) {
                newGraph.add(new Edge<>(edge.getDest(), edge.getSource()));
                newGraph.add(new Edge<>(edge.getSource(), edge.getDest()));
            }
        }
    }
    return newGraph;
}
/**
     * 通过原图的顶点重新创建一个图,但不保留其边
     */
private static <T> DGraph<T> getNoEdgeGraph(DGraph<T> graph) {
    DGraph<T> newGraph = new ListDGraph<>();
    Iterator<T> iterator = graph.iterator(ITERATOR_TYPE_BFS, graph.get(0).getValue());
    while (iterator.hasNext()) {
        newGraph.add(new Vertex<>(iterator.next()));
    }
    return newGraph;
}
/**
     * 根据边的权值创建一个优先队列
     */
private static <T> BinaryHeap<Edge<Vertex<T>>> makePriorityQueue(DGraph<T> graph) {
    BinaryHeap<Edge<Vertex<T>>> priorityQueue = new BinaryHeap<>();
    for (int i = 0; i < graph.size(); i++) {
        Vertex<T> ve = graph.get(i);
        List<Edge<Vertex<T>>> edgeList = ve.getEdgeList();
        if (edgeList != null
            && !edgeList.isEmpty()) {
            for (Edge<Vertex<T>> edge : edgeList) {
                priorityQueue.insert(edge);
            }
        }
    }
    return priorityQueue;
}
/**
     * 当前图加上这两个顶点后是不是连通的
     */
private static <T> boolean connected(DGraph<T> graph, Vertex<T> ve1, Vertex<T> ve2) {
    Iterator<T> iterator = graph.iterator(ITERATOR_TYPE_BFS, ve1.getValue());
    while (iterator.hasNext()) {
        if (iterator.next().equals(ve2.getValue())) {
            return true;
        }
    }
    return false;
}

上面的优先队列:BinaryHeap 可点击这里查看源码,或者点击下面的链接:
https://github.com/0xZhangKe/Algorithms/blob/master/src/com/zhangke/java/graph/adt/heap/BinaryHeap.java

更多其他算法及数据结构相关的知识可去我的 Github 仓库查看:
https://github.com/0xZhangKe/Algorithms

如果觉得还不错的话,欢迎关注我的个人公众号,我会不定期发一些干货文章~
公众号二维码

也可以加我微信:
微信二维码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值