A Bit of History
In 1980, Harold N. Gabow published "Path-based depth-first search for strong components" in the Journal on Computing. While Tarjan's 1972 algorithm pioneered single-pass $O(V+E)$ SCC identification using lowlink integers, and Kosaraju's 1978 algorithm required two full DFS passes and a transposed graph, Gabow realized that strongly connected components could be identified purely through stack manipulation. By maintaining a vertex stack and a boundary stack, Gabow eliminated integer min() updates on low-link arrays entirely, producing a clean, path-based algorithm.
Working Principle: Two Stacks
Gabow's algorithm performs a single DFS while maintaining two stacks:
- Vertex Stack ($S$): Holds all vertices currently being explored that have not yet been assigned to a completed SCC.
- Boundary Stack ($P$): Holds the "root" vertices of candidate SCCs currently on the DFS search path.
When traversing from node $u$ to neighbor $v$:
- If $v$ is unvisited: push $v$ onto $S$ and $P$, then recurse on $v$.
- If $v$ is already visited and still on stack $S$ (a back edge or cross edge to an active component): pop from $P$ all vertices whose discovery order is greater than $v$'s discovery order. This collapses the detected cycle into a single component boundary!
- When the DFS call for node $u$ completes: if $u$ is at the top of stack $P$, then $u$ is the root of an SCC. Pop $u$ from $P$, and pop all vertices from $S$ down through $u$ — they form a complete SCC!
Key Insight
Stack $P$ contracts cycles directly! When a back-edge to an active vertex $v$ is found, popping stack $P$ until $v$ is at the top merges all vertices in the cycle into a single SCC root boundary without needing lowlink[u] = min(...) arithmetic.
Worked Example
Consider a directed graph with cycle $1 \to 2 \to 3 \to 1$ and edge $3 \to 4$:
- DFS visits 1, 2, 3: $S = [1, 2, 3]$, $P = [1, 2, 3]$.
- Edge $3 \to 1$: 1 is on stack $S$. Pop from $P$ until top is 1 $\implies$ $P = [1]$. The cycle $\{1, 2, 3\}$ is merged under root 1.
- DFS visits 4: $S = [1, 2, 3, 4]$, $P = [1, 4]$.
- DFS for 4 finishes: top of $P$ is 4. Pop 4 from $P$ and $S$. SCC 1: $\{4\}$.
- DFS for 1 finishes: top of $P$ is 1. Pop from $S$ down to 1. SCC 2: $\{3, 2, 1\}$.
Correctness & Invariants
The algorithm maintains the invariant that stack $P$ contains the entry points of all maximal strongly connected subgraphs in the current DFS tree. Whenever a cycle is completed by an edge back to an ancestor $v$, popping $P$ down to $v$ maintains the exact boundaries of active SCCs. Because every vertex is pushed and popped from $S$ and $P$ at most once, correctness is guaranteed.
Complexity Analysis
Like Tarjan's and Kosaraju's algorithms, Gabow's runs in optimal linear time:
$$\text{Time Complexity: } O(V + E) \qquad \text{Space Complexity: } O(V)$$
Because Gabow's algorithm avoids low-link array comparisons and assignment overhead, it often exhibits slightly smaller constant factors and fewer memory writes in benchmark tests.
Implementation
Real-World Applications
2-SAT Solvers & Formal Verification
2-SAT (2-Satisfiability) problems are solved in $O(V+E)$ time by building an implication graph and finding its SCCs. Gabow's algorithm is a favorite in high-performance formal verification tools because its stack-contraction logic avoids arithmetic arrays.
Exercises
- Trace Gabow's algorithm on a 4-cycle graph $0 \to 1 \to 2 \to 3 \to 0$. Show the contents of stacks $S$ and $P$ at every step.
- Compare Gabow's 2-stack approach with Tarjan's 1-stack +
lowlinkapproach. - Show how the output of Gabow's algorithm gives a topological ordering of the condensation DAG.
- Challenge: Modify Gabow's algorithm to compute biconnected components on undirected graphs.
Limitations
Recursion Depth & Stack Memory
Like Tarjan's algorithm, Gabow's uses deep recursion during DFS. On extremely deep graphs (e.g. line graphs with $N = 10^6$), an iterative stack-based DFS implementation is required to prevent call-stack overflow.