1. 程式人生 > 其它 >【演算法4】4.3.最小生成樹

【演算法4】4.3.最小生成樹

最小生成樹

加權圖是一種為每條邊關聯一個權值或是成本的圖模型。

最小生成樹:圖的生成樹是它的一棵含有其所有頂點的無環連通子圖。
一幅加權圖的最小生成樹(MST)是它的一棵權值(樹中所有邊的權值之和)最小的生成樹。

計算最小生成樹的兩種經典演算法:

  • Prime 演算法
  • Kruskal 演算法

抽象資料型別

加權邊 API

public class Edge implements Comparable<Edge> {
    Edge(int v, int w, double weight); // 構造器
    
    double weight(); // 邊的權重
    int either(); // 邊兩端的頂點之一
    int other(int v); // 另一個頂點
    int compareTo(Edge that);
    String toString();
}

加權無向圖 API

public class EdgeWeightedGraph {
    EdgeWeightedGraph(int V); // 建立一幅含有 V 個頂點的空圖
    EdgeWeightedGraph(In in); // 從輸入流讀取圖

    int V(); // 頂點數
    int E(); // 邊數
    void addEdge(Edge e); // 新增一條邊
    Iterable<Edge> adj(int v); // 鄰接邊
    Iterable<Edge> edges(); // 所有邊
    String toString();
}

最小生成樹 API

public class MST {
    MST(EdgeWeightedGraph G);

    Iterable<Edge> edges(); // 最小生成樹的所有邊
    double weight(); // 最小生成樹的權重

}

Prim 演算法

Prim 又叫普里姆演算法,其應用了貪心演算法思想,即每次選擇權重最小的橫切邊加入樹中。

思想:Prim 演算法的每一步都會為一棵生長中的樹新增一條邊。
一開始這棵樹只有一個頂點,然後會向它新增 V-1 條邊,每次總是將下一條連線樹中的頂點與不在樹中的頂點且權重最小的邊加入樹中(即由樹種的頂點所定義的切分中的一條橫切邊)。

Prim 演算法實現

/**
 * 最小生成樹 Prime 演算法的延遲實現(失效的邊是延遲刪除的)
 * * 使用 marked 陣列標記最小生成樹的頂點
 * * 使用儲存最小生成樹的邊
 * * 使用優先佇列儲存橫切邊
 * */
public class LazyPrimMST {
    private boolean[] marked; // 最小生成樹的頂點
    private Queue<Edge> mst; // 最小生成樹的邊
    private MinPQ<Edge> pq; // 橫切邊(包括失效的邊)

    public LazyPrimMST(EdgeWeightedGraph G) {
        marked = new boolean[G.V()];
        mst = new Queue<>();
        pq = new MinPQ<>();
        // 起點
        visit(G, 0);
        // Prim 演算法
        while (!pq.isEmpty()) {
            Edge e = pq.remove();
            int v = e.either();
            int w = e.other(v);
            if (marked[v] && marked[w]) continue; // 跳過失效的邊
            mst.add(e);
            if (!marked[v]) visit(G, v);
            if (!marked[w]) visit(G, w);
        }
    }

    private void visit(EdgeWeightedGraph G, int v) {
        marked[v] = true;
        for (Edge e : G.adj(v)) {
            if (!marked[e.other(v)]) pq.add(e);
        }
    }

    public Iterable<Edge> edges() {
        return mst;
    }

    public double weight() {
        double weight = 0.0;
        for (Edge e : mst) {
            weight += e.weight();
        }
        return weight;
    }
}