Skip to main content

Advanced Graph Algorithms

Beyond Basic Graphs

Once you’ve mastered BFS, DFS, and shortest paths, it’s time for advanced graph techniques. These algorithms appear in harder CF problems (1700+) and ICPC regionals.
Pattern Recognition Signals:
  • “Strongly connected” → SCC (Kosaraju/Tarjan)
  • “Bridge/cut vertex” → Bridge finding algorithm
  • “Boolean satisfiability” → 2-SAT
  • “Bipartite matching” → Hungarian/Kuhn’s algorithm
  • “Maximum flow” → Ford-Fulkerson/Dinic

Strongly Connected Components (SCC)

An SCC is a maximal set of vertices where every vertex is reachable from every other. Intuition: In a directed graph, SCCs are “clusters” where you can get from any node to any other within the cluster. The graph of SCCs forms a DAG (no cycles possible, since any cycle would merge SCCs). Why it matters:
  • Simplify complex graphs by collapsing SCCs to single nodes (condensation)
  • Solve 2-SAT problems
  • Analyze dependencies and reachability

Kosaraju’s Algorithm

The Insight: If we process nodes in order of decreasing finish time from a DFS, and run DFS on the reversed graph, each DFS tree corresponds to an SCC. Why does reversing work? In the original graph, if there’s a path from SCC A to SCC B, then in the reversed graph there’s a path from B to A. By processing in finish-time order, we ensure we start from “sink” SCCs (no outgoing edges to other SCCs), so DFS on reversed graph stays within one SCC. Algorithm:
  1. Run DFS on original graph, recording finish order (node goes on stack when its DFS completes)
  2. Process nodes in reverse finish order, running DFS on reversed graph
  3. Each DFS tree in step 2 is an SCC

Tarjan’s Algorithm

Single DFS, more efficient in practice. Key Concepts:
  • disc[u]: Discovery time of node u (when we first visit it)
  • low[u]: Lowest discovery time reachable from u’s subtree via tree edges and at most one back edge
  • onStack: Whether node is in the current DFS path (potential SCC members)
When is u the root of an SCC? When low[u] == disc[u]. This means no node in u’s subtree can reach anything discovered before u, so u and everything on the stack down to u form an SCC. Visual Example:

Bridges and Articulation Points

Finding Bridges

An edge is a bridge if removing it disconnects the graph. Key Insight: Edge (u, v) where u is the parent of v in DFS tree is a bridge if and only if low[v] > disc[u]. This means v cannot reach u or any ancestor of u through any path other than (u, v). Why low[v] > disc[u] and not >=?
  • If low[v] == disc[u], v can reach u through another path (a back edge from v’s subtree to u)
  • Such a back edge would give us an alternative path, so (u, v) wouldn’t be a bridge
Visual Example:
Handling Parallel Edges: We track edge IDs, not just parent nodes, to correctly handle multiple edges between same vertices.

Finding Articulation Points

A vertex is an articulation point if removing it disconnects the graph. Two Cases:
  1. Root of DFS tree: u is an AP iff it has 2+ children in the DFS tree. If root has only one child, removing root leaves one connected component.
  2. Non-root node: u is an AP iff some child v has low[v] >= disc[u]. This means v (and its subtree) cannot reach any ancestor of u—so removing u would disconnect v’s subtree.
Why >= for APs but > for bridges?
  • For bridges: we’re removing an edge, so even if v can reach u via another path, the graph stays connected
  • For APs: we’re removing vertex u itself, so even if v can reach exactly u (low[v] = disc[u]) via a back edge, that doesn’t help—u is still gone!
Visual Example:

2-SAT

Solve boolean satisfiability with clauses of form (x OR y). The Problem: Given n boolean variables and m clauses of form “(a OR b)”, find an assignment that satisfies all clauses, or report impossibility. The Key Transformation: Each clause (ab)(a \lor b) is equivalent to two implications:
  • ¬ab\neg a \Rightarrow b (if a is false, b must be true)
  • ¬ba\neg b \Rightarrow a (if b is false, a must be true)
Building the Implication Graph:
  • Create 2n nodes: for each variable x, one node for x=true and one for x=false
  • For clause (a OR b): add edges (NOT a → b) and (NOT b → a)
Why SCC? If x and NOT x are in the same SCC, they imply each other:
  • x ⇒ … ⇒ NOT x ⇒ … ⇒ x
  • This means x ⇔ NOT x, which is a contradiction!
Finding the Assignment:
  1. Find all SCCs using Tarjan/Kosaraju
  2. If any variable has both states in same SCC → UNSATISFIABLE
  3. Otherwise: in topological order of SCCs, assign TRUE to variables whose TRUE-node comes after FALSE-node (later in topo order = “stronger”)
Common Clause Patterns:

2-SAT Example


Bipartite Matching

Kuhn’s Algorithm (Hungarian)

The Idea: For each left-side node, try to find an augmenting path—a path that alternates between unmatched and matched edges. If we find one, we can “flip” all edges along it, increasing the matching by 1. The DFS greedily tries to match each left node, and if a right node is already matched, it recursively tries to re-match the current partner elsewhere. Complexity: O(V * E) in the worst case. For dense bipartite graphs, Hopcroft-Karp is faster at O(E * sqrt(V)), but Kuhn is simpler and usually sufficient for CP.

Maximum Flow (Dinic’s Algorithm)

The Problem: Given a directed graph with edge capacities, find the maximum flow from source s to sink t. Why Dinic over Ford-Fulkerson? Ford-Fulkerson with DFS can take O(V * E^2) or even fail to terminate with irrational capacities. Dinic uses BFS to build level graphs and DFS to find blocking flows, achieving O(V^2 * E) in general and O(E * sqrt(V)) on unit-capacity graphs. For bipartite matching, Dinic with max-flow is often as fast as Hopcroft-Karp. When to use max flow: Anytime you see “maximum number of edge-disjoint paths,” “minimum cut,” or “maximum matching in general graphs.” The max-flow min-cut theorem says the maximum flow equals the minimum cut—this duality is incredibly useful.

Eulerian Path/Circuit

A path that visits every edge exactly once (contrast with Hamiltonian path, which visits every vertex exactly once—an NP-complete problem). Analogy: The classic “Seven Bridges of Konigsberg” problem. Euler proved in 1736 that you cannot cross all seven bridges exactly once and return to the start — because four landmasses had odd degree. This was the birth of graph theory. Existence conditions (for undirected graphs):
  • Eulerian circuit (returns to start): all vertices have even degree, and the graph is connected
  • Eulerian path (start != end): exactly 2 vertices have odd degree (those are the start and end), and the graph is connected
  • Neither exists: more than 2 vertices have odd degree
For directed graphs: circuit exists if all vertices have in-degree = out-degree; path exists if at most one vertex has out-degree - in-degree = 1 (start) and at most one has in-degree - out-degree = 1 (end). Edge case: Isolated vertices (degree 0) do not affect Euler path/circuit existence — they simply are not visited. But the “connected” requirement applies to the subgraph induced by vertices with nonzero degree.

Practice Problems

Intermediate (1400-1700)

Advanced (1700-2000)


Key Takeaways

SCC

Kosaraju or Tarjan for strongly connected components.

Bridges/APs

Use low-link values from DFS tree.

2-SAT

Model as implication graph, solve with SCC.

Max Flow

Dinic’s O(V²E) for general, O(E√V) for bipartite.

Next Up

Back to Overview

Return to the complete course curriculum.