I'll describe polylogarithmic solution to the problem. Let's introduce some definitions. We'll denote:
- Set of graph's vertices by
V
, set of graph's edges by E
and set of MST edges by T
.
- Edge of graph between vertices
v
and u
by {v, u}
.
- 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:
x
does not belong to T
.
- 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)
:
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
.
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.