美文网首页Android开发Java 杂谈算法
最小生成树-Prim 算法与 Kruskal 算法 Java 实

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

作者: 张可_ | 来源:发表于2019-04-28 21:43 被阅读13次

    最小生成树

    一个无向图 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

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


    公众号二维码

    也可以加我微信:


    微信二维码

    相关文章

      网友评论

        本文标题:最小生成树-Prim 算法与 Kruskal 算法 Java 实

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