- Definition: A node in a connected graph is called an articulation
point if the deletion of that node disconnects the graph.
- Definition: A connected graph is called biconnected if it has
no articulation points. That is, the deletion of any single node leaves
the graph connected.
LI> In the case of networks, an articulation point is referred to
as a single point of failure.
- The Biconnectivity Problem:
- Input: a connected graph G
- Problem: Determine whether or not the graph is
biconncted. If not biconnected, find all the articulation points.
- DFS on a connected graph G yields a DFS tree whose edges
are from the graph. Draw those edges as straingt edges. Add the remaining
edges of the graph as dashed edges in the tree.
- Theorem: Each dashed edge goes from an descendent to an
ancestor. For that reason, the dashed edges are called backward edges
(or simply back edges).
- Proof: The proof is by contradiction.
Let (x,y) be a dashed
edge between nodes that are not ancestor-descendent, that is, x and
y are in separate subtrees of the DFS tree. Assume x was visited
before y.
At the very last time (say time t) when a search for unvisited
neighbors of x was conducted and none found, x was backtracked from,
never to return to x again.
Well, since y is not a descendent of
x, y is not visited at time t.
But since y is a neighbor of x
and y is not visited at time t, y would have to be visited from x before
the algorithm backtracks from x. That would make y a descendent of x.
Contradiction.
Therefore, no such cross edge (x,y) can exist in a DFS tree.
Q.E.D.
- Observations:
- the DFS tree along with the back edges form a new
layout of the entire graph. In other terms, we don't have to
refer to the origfinal layout of G anymore.
- If the root has more than one child, then the root
is an articulation point. That is because the removal
of the root makes the subtrees of that root
disconnected from one another since there are no
cross dashed edges between them.
- On the other hand, if the root has a single child,
removing the root leaves a single tree in place, and
thus the remaining graph is still connected.
- This suggests a first algorithm for identifying
articulation points: Do a DFS from each node,
and check that node to see if it has more than one child.
This algorithm, however, takes O(n|E|) time.
A better algorithm will be designed that takes only
O(|E|) time.
- The nodes of the graph will be relabeled so that the new labels
carry meaningful information. Indeed, each node i will
have two new labels: DFN[i] and L[i].
- DFN[i] is the time at which i is visited. Thus, the first node
visited (i.e., the root) has its DFN = 1. The second
node visited has a DFN = 2, and so on.
- To define L[i], we need to define the notion of a special
path.
- A special path from a node x is a path that goes down from
x zero or more tree edges, and then follows a single backward edge.
Also, a nil path from a node to itself is called special.
- This definition of special paths is motivated by the very
important observation that a non-root node x is an articulation point
if and only if x has a subtree from which no backward edge originates
and ends at a proper ancestor of x.
- The above observation can be proved by contradiction. If from
every subtree of x there is a backward edge that goes to a proper ancestor
of x, then the removal of x (and its incident edges) leaves the
subtrees of x connected to the remainder of the tree by those
backward edges. That is, the graph remains connected, implying that
x is not an articulation point.
- The latest observation can be re-stated as follows:
x is an articulation point if and only if
there exists an child w of
x such that every special path from w ends up at x or below
x.
- This is equivalent to saying that
x is an articulation point if and only if
there exists an child w of
x such that the highest node reachable from w by a special
path is x or lower than x.
- This last re-statement of the condition is a geometrical
statement
(involving notions of geometrical "high" and "low" positions).
- To make the geometric condition algorithmically testable, we will
convert it into an equivalent algebraic condition.
- Clearly, if a node y is a lower descendent of a node x,
then DFN[y] > DFN[x].
- It remains to "quantify" the notion of the highest reachable
node by a special path from w. The DFN of such a node will be referred
to as L[w]. Since highest (gemetrically) means smallest DFN
(algebraically), we can formally quantify L[w] as follows:
- L[w]=min{DFN[y] | y is reachable from w by a special path}
- The geometric condition translates then to
x is an articulation point if and only if x has a child w
such that L[w] >= DFN[x]
- The DFNs are easy to compute using DFN values. It remains to
develop
a method to compute the L values.
- Notice that
L[x]=min{
DFN[x],
{DFN[y] | (x,y) is a back edge},
{DFN[y] | y is reachable from some child w of x by a special
path} for each child w of x}
}
- Since the minimum of the whole is the minimum of the minimums
of the parts, we conclude that
- L[x]=min{
DFN[x],
{DFN[y] | (x,y) is a back edge},
{min{DFN[y] | y is reachable from some child w of x by a special
path} for each child w of x}
}
- This implies that
L[x]=min{
DFN[x], {DFN[y] | (x,y) is a back edge},
{min{L[w] | for each child w of x}
}
- The latter definition allows for a bottom computation of L[x]
using DFS. Once the Ls of the children have been computed, L[x]
can easily be derived.
- The computation of L[x], being a minimum, will be carried
out in progression: we initialize L[x] to DFN[x], and thereafter,
everytime a relevant term (to be minimized over) becomes available,
the value of L[x] is updated by comparing it to that term and assigning
the term to L[x] if the term is smaller.
- The Algorithm for finding the articulation points is therefore
based on DFS. The algorithm is presented next, as a modification
of the DFS algorithm. The changes are put in green color.
Procedure DFS(input: graph G,)
begin
Stack S;
Integer s,x;
Integer DFN[1:n], L[1:n], Parent[1:n];
Integer num := 1;
s := an unvisited node;
visit(s);
push(s,S);
DFN[s] := num; num++;
L[v] := DFN[v];
While (S is not empty) do
x := top(S);
if (x has an unvisited neighbor y) then
visit(y);
push(y,S);
DFN[y] := num; num++;
Parent[y] := x;
L[y] := DFN[y];
else
pop(S);
for (every neighbor y of x) do
if (y != parent[x] and
DFN[y] < DFN[x]) then
/* y is an ancestor of x,
and (x,y) is a back edge*/
L[x] := min(L[x],DFN[y]);
else
if (x = Parent[y]) then
L[x] : min(L[x],L[y]);
if (L[y] >= DFN[x] and
x is not root) then
x is an articulation point;
endif
endif
endif
endfor
endif
endwhile
if (s has more than one child) then
s is an articulation point;
endif
end
- Time Complexity:
The new statements add constant-time operations except
for the new for loop at the time of backtracking.
This new for-loop crosses the edges one more time
to update the L values and check for articulation points.
This increases the time by another O(|E|).
The final if-statement to check for the status of the root
can be done by scanning the Parent array to count the number
of children of the root s ( by counting the number of nodes
whose Parent is s). This takes O(n) = O(|E|) time.
Therefore, the time complexity of the whole algorithm
is O(|E|).