4.3 Minimum Spanning Tree 笔记+理解

  • Given a connected undirected graph, a spinning tree is a connected, acyclic subgraph that contains all the vertices;
  • and a MST is a spinning tree that has the min. weights.

Cut:

  • A cut partitions the vertices into two nonempty sets;
  • A crossing edge connects the two sets partitioned by the cut;
Cut property:

The crossing edge that has the min. weight of a cut is in the MST.
Pf: Given a cut, assume the min. crossing edge e is not in the MST.

  • Adding e to the MST will form a cycle, and in that cycle, there must be a crossing edge f of that cut.
  • Replace e with f will still form a spanning tree, but now the newly formed spanning tree has less weight than the original MST.
  • So contradiction.


    image

Greedy Algorithm for MST

  • start with all edges uncolored
  • choose a cut that has no colored crossing edge, color the min. weight edge
  • until there are V-1 colored edges

Pf [correctness]:

  • edges colored are in the MST (cut property)
  • if there're less than V-1 edges, then there must be cuts that have uncolored crossing edges (the graph is connected)

Implementations:

Prim's Algorithm:
  • Start from any of the vertices,
  • greedily growing the tree so that the next edge added would be the min. weight edge that connects the existing MST to the unvisited vertices \Rightarrow color the min. crossing edge in a cut that partitions the MST and the unvisited vertices.
  • until there're V-1 edges
    Pf [correctness]:
    It's just a special case for the greedy algorithm for the MST!!
Lazy Prim's Implementation

Edge.java

Edge API

EdgeWeightedGraph.java

EdgeWeightedGraph API

Public void LazyPrimMST(EdgeWeightedGraph G) {
    boolean[] marked = new boolean[G.V()];
    MinPQ<Edge> pq = new MinPQ<>();
    Queue<Edge> mst = new Queue<>();
    visit(G, 0);
    while (!pq.isEmpty()) {
        Edge e = pq.delMin();
        int v = e.either(); int w = e.other(v);
        if (marked[v] && marked[w]) { continue; }
        if (!marked[v]) { visit(G, v); }
        if (!marked[w]) { visit(G, w); }
    }
}

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

Running time:

  • At most E edges in the priority queue \Rightarrow takes ~\log{E} to insert(), ~2\log{E} to delMin()
  • At most E edges to insert and delete, so ~E\log{E}
Eager Prim's Implementation:

Every time the tree grows (adding v to the MST), update the min. weight edges between the no-tree vertices adj. to v and the tree.

When we add a vertex v to the tree, the only possible change with respect to each non- tree vertex w is that adding v brings w closer than before to the tree. In short, we do not need to keep on the priority queue all of the edges from w to tree vertices—we just need to keep track w of the minimum-weight edge and check whether the addition of v to the tree necessitates that we update that minimum (be- cause of an edge v-w that has lower weight), which we can do as we process each edge in v’s adjacency list.

public void EagerPrimMST(EdgeWeightedGraph G) {
    marked = new boolean[G.V()];
    // min. distance from a non-tree vertex v to the MST
    distTo = new double[G.V()]; 
    for (int v = 0; v < G.V(); v++) {
        distTo[v] = INFINITY;
    }
    // min. weight edge from a non-tree vertex v to the MST
    edgeTo = new Edge[G.V()];

    // key is vertex
    // priority is the min. distance to the tree
    pq = new IndexMinPQ<Double>(G.V());

    pq.insert(0, 0.0); // start from vertex 0
    distTo[0] = 0.0;
    while (!pq.isEmpty()) {
        // Add closest vertex to tree.
        visit(G, pq.delMin());
    }
}

private void visit(... G, int v) {
    marked[v] = true;
    for (Edge e : G.adj(v)) {
        int w = e.other(v)
        if (marked[e.other(v)]) { continue; }
        if (e.weight() < distTo[w]) {
            // update if there's closer path from w to the tree
            edgeTo[w] = e;
            distTo[w] = e.weight();
            // update the index pq
            if (pq.contains(w)) { pq.change(w, e.weight()); } // at most E times
            else { pq.insert(w, e.weight()); } // at most V times
        }
    }
}

Running time:

  • pq contains at most V, so pq operation ~\log{V}
  • V inserts, V deletes, and E changes
  • total ~E\log{V}
Kruskal's Algorithm:
  • sort the edges by weights in ascending order
  • add the edges to the tree in the order as long as the edge to be added wouldn't form a cycle
  • until V-1 edges are added

how to check cycle?
\Rightarrow use Union Find ~ lg V

  • if vertex v and w are already connected, then adding the edge v-w will form a cycle
  • if not, v-w will union the two sets
public void KruskalMST(... G) {
    MinPQ<Edge> pq = new MinPQ<>();
    UF uf = new UF(G.V())
    for (Edge e : G.E()) { // ~ElogE
        pq.insert(e);
    }
    while (!pq.isEmpy()) { // E times
        Edge e = pq.delMin(); // ~ log E
        int v = e.either(); int w = e.other(v);
        if (!uf.connected(v, w)) { // ~ lg V
            mst.enqueue(e);
            uf.connect(v, w);  // ~ lg V
        }
    }
}

Running time: ~ E\log{E}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容