Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Faster second-best MST algorithm?

I am struggling with this.

We can get MST using Kruskal's algorithm or Prim's algorithm for the MST.

And for "second-best" MST, I can:

  1. first get MST using either of the algorithm mentioned above.
  2. For each V-1 of the optimal edge from the MST:
    a. first remove or flag the edge
    b. continue calculating MST without that edge
    c. compare and record down that "second-best" MST with previous iteration
  3. In the end we have "second-best" MST

But this runs in O(VE) where V is num of vertex and E is number of edges.

How can get a speed up using Union-find disjoint set or LCA(lowest common ancester) ?

hints, pseodo code, or web links pointers.

Any help would be highly appreciated! Thanks:)

like image 473
noooooooob Avatar asked Mar 01 '14 03:03

noooooooob


2 Answers

I'll describe polylogarithmic solution to the problem. Let's introduce some definitions. We'll denote:

  1. Set of graph's vertices by V, set of graph's edges by E and set of MST edges by T.
  2. Edge of graph between vertices v and u by {v, u}.
  3. Weight of edge e by W(e) and weight of MST by W(T).

Let's consider function MaxEdge(v, u), which is equal to the edge with the largest weight on the simple path between v and u that belongs to T. If there are several edges with maximum weight MaxEdge(v, u) can be equal to any of them.

To find the second best MST, we need to find such edge x = {p, q}, that:

  1. x does not belong to T.
  2. Function W(x) - W(MaxEdge(p, q)) is minimal possible.

It's possible to prove that the second best MST can be constructed by removing MaxEdge(p, q) from T and then adding x = {p, q} to T.

Now let's build a data structure that will be able to compute MaxEdge(p, q) in O(log|V|).

Let's pick a root for the tree T (it can be any vertex). We'll call the number of edges in the simple path between vertex v and the root - the depth of vertex v, and denote it Depth(v). We can compute Depth(v) for all vertices in O(|V|) by depth first search.

Let's compute two functions, that will help us to calculate MaxEdge(p, q):

  1. Parent(v, i), which is equal to the vertex that is a parent (might be non direct parent) of vertex v with depth equal to Depth(v) - 2^i.
  2. MaxParentEdge(v, i), which is equal to MaxEdge(v, Parent(v, i)).

Both of them can be computed by a recurrence function with memorisation in O(|V|log|V|).

// Assumes that 2^i <= Depth(v)
Vertex Parent(Vertex v, Vertex i) {
    if (i == 0) return direct_parent[v];
    if (Memorized(v, i)) return memorized_parent[v][i]; 
    memorized_parent[v][i] = Parent(Parent(v, i - 1), i - 1);
    return memorized_parent[v][i];
}

Edge MaxParentEdge(Vertex v, Vertex i) {
    if (i == 0) return Edge(v, direct_parent[v]);
    if (Memorized(v, i)) return memorized_parent_edge[v][i]; 
    Edge e1 = MaxParentEdge(v, i - 1);
    Edge e2 = MaxParentEdge(Parent(v, i - 1), i - 1);
    if (W(e1) > W(e2)) {
        memorized_parent_edge[v][i] = e1;
    } else {
        memorized_parent_edge[v][i] = e2;
    }
    return memorized_parent_edge[v][i];
}

Before we are ready to compute MaxEdge(p, q), let's introduce the final definition. Lca(v, u) will denote lowest common ancestor of vertices v and u in the rooted tree T. There are a lot of well known data structures that allows to compute Lca(v, u) query in O(log|V|) or even in O(1) (you can find the list of articles at Wikipedia).

To compute MaxEdge(p, q) we will divide the path between p and q into two parts: from p to Lca(p, q), from Lca(p, q) to q. Each of these parts looks like a path from a vertex to some of its parents, therefore we can use our Parent(v, i) and MaxParentEdge(v, i) functions to compute MaxEdge for these parts.

Edge MaxEdge(Vertex p, Vertex q) {
    Vertex mid = Lca(p, q);
    if (p == mid || q == mid) {
       if (q == mid) return QuickMaxEdge(p, mid);
       return QuickMaxEdge(q, mid);
    }
    // p != mid and q != mid
    Edge e1 = QuickMaxEdge(p, mid);
    Edge e2 = QuickMaxEdge(q, mid);
    if (W(e1) > W(e2)) return e1;
    return e2;
}

Edge QuickMaxEdge(Vertex v, Vertex parent_v) {
    Edge maxe = direct_parent[v];
    string binary = BinaryRepresentation(Depth(v) - Depth(parent_v));
    for (int i = 0; i < binary.size(); ++i) {
        if (binary[i] == '1') {
            Edge e = MaxParentEdge(v, i);
            if (W(e) > W(maxe)) maxe = e;
            v = Parent(v, i);
        }
    }
    return maxe;
}

Basically function QuickMaxEdge(v, parent_v) does jumps of length 2^i to cover distance between parent_v and v. During a jump it uses precomputed values of MaxParentEdge(v, i) to update the answer.

Considering that MaxParentEdge(v, i) and Parent(v, i) is precomputed, MaxEdge(p, q) works in O(log|V|), which leads us to an O(|E|log|V|) solution to the initial problem. We just need to iterate over all edges that does not belong T and compute W(e) - MaxEdge(p, q) for each of them.

like image 143
Gerald Agapov Avatar answered Sep 21 '22 15:09

Gerald Agapov


Let V be the vertex set and E be the edge set.

Let T be the MST obtained using any of the standard algorithms.

Let maxEdgeInPath(u,v) be the maximum edge on the unique path in T from vertex u to vertex v.

For each vertex u perform BFS on T. This gives maxEdgeInPath(u,x) for all x belonging to V-u.

Find an edge (x,y) which does not belong to T that minimizes w(x,y) - w(maxEdgeInPath(x,y))

Weight of 2ndMST is W(T) + w(x,y) - maxEdgeInPath(x,y)

This is based on the algorithm provided in this link. I'm not sure if this is correct and I hope someone would add a proof here.

Compexity: To compute BST for 1 vertex takes O(V+E) = O(V) as E = V-1 in T Hence overall time complexity is O(V^2)

like image 22
arunmoezhi Avatar answered Sep 21 '22 15:09

arunmoezhi