Advanced Graph Algorithms
此内容尚不支持你的语言。
Intuition
Section titled “Intuition”Finding pockets of mutual reachability: Strongly connected components are like friend groups where everyone knows everyone, within an SCC, you can reach any node from any other node. Condensing SCCs into single nodes reveals the DAG structure of the graph.
Why it matters: SCCs are fundamental to understanding graph structure, they identify cyclic dependencies in build systems, strongly connected regions in social networks, and mutually reachable states in automata.
The key insight: Kosaraju algorithm uses two DFS passes, the first determines the order to process nodes, the second finds SCCs on the transposed graph. This elegant two-pass approach runs in O(V+E) time.
Strongly Connected Components
Section titled “Strongly Connected Components”A strongly connected component (SCC) of a directed graph is a maximal set of vertices such that there is a path from every vertex to every other vertex within the set.
Kosaraju’s Algorithm
Section titled “Kosaraju’s Algorithm”Kosaraju’s algorithm finds all SCCs in time using two DFS passes.
def kosaraju(n, adj): """ Find strongly connected components using Kosaraju's algorithm. Time: O(V + E) Space: O(V + E) Returns: list of SCCs (each SCC is a list of vertices) """ visited = [False] * n order = []
def dfs1(v): visited[v] = True for u in adj[v]: if not visited[u]: dfs1(u) order.append(v)
for v in range(n): if not visited[v]: dfs1(v)
radj = [[] for _ in range(n)] for v in range(n): for u in adj[v]: radj[u].append(v)
visited = [False] * n sccs = []
def dfs2(v, component): visited[v] = True component.append(v) for u in radj[v]: if not visited[u]: dfs2(u, component)
for v in reversed(order): if not visited[v]: component = [] dfs2(v, component) sccs.append(component)
return sccsTarjan’s Algorithm
Section titled “Tarjan’s Algorithm”Tarjan’s algorithm finds SCCs in a single DFS pass using a stack and low-link values.
def tarjan_scc(n, adj): """ Find SCCs using Tarjan's algorithm. Time: O(V + E) Space: O(V) """ index_counter = [0] stack = [] on_stack = [False] * n index = [-1] * n low = [0] * n sccs = []
def strongconnect(v): index[v] = index_counter[0] low[v] = index_counter[0] index_counter[0] += 1 stack.append(v) on_stack[v] = True
for w in adj[v]: if index[w] == -1: strongconnect(w) low[v] = min(low[v], low[w]) elif on_stack[w]: low[v] = min(low[v], index[w])
if low[v] == index[v]: scc = [] while True: w = stack.pop() on_stack[w] = False scc.append(w) if w == v: break sccs.append(scc)
for v in range(n): if index[v] == -1: strongconnect(v)
return sccsDAG Condensation
Section titled “DAG Condensation”The condensation of a directed graph is the DAG formed by contracting each SCC to a single vertex. The condensation DAG is useful for solving problems on the original graph.
def build_condensation(n, adj, sccs): """ Build condensation DAG from SCCs. Time: O(V + E) Space: O(V + E) """ scc_id = [0] * n for i, scc in enumerate(sccs): for v in scc: scc_id[v] = i
num_sccs = len(sccs) cond_adj = [set() for _ in range(num_sccs)] for v in range(n): for u in adj[v]: if scc_id[v] != scc_id[u]: cond_adj[scc_id[v]].add(scc_id[u])
return [list(s) for s in cond_adj], scc_idgraph TD
subgraph "Original Graph"
A1["A"] --> B1["B"]
B1 --> C1["C"]
C1 --> A1
C1 --> D1["D"]
D1 --> E1["E"]
E1 --> F1["F"]
F1 --> D1
F1 --> G1["G"]
end
subgraph "Condensation DAG"
SCC1["SCC1: A,B,C"] --> SCC2["SCC2: D,E,F"]
SCC2 --> SCC3["SCC3: G"]
endBridges and Articulation Points
Section titled “Bridges and Articulation Points”Bridges (Cut Edges)
Section titled “Bridges (Cut Edges)”A bridge is an edge whose removal increases the number of connected components.
def find_bridges(n, adj): """ Find all bridges in an undirected graph. Time: O(V + E) Space: O(V) Returns: list of (u, v) bridges """ visited = [False] * n tin = [0] * n low = [0] * n timer = [0] bridges = []
def dfs(v, parent): visited[v] = True tin[v] = low[v] = timer[0] timer[0] += 1 for u in adj[v]: if u == parent: continue if visited[u]: low[v] = min(low[v], tin[u]) else: dfs(u, v) low[v] = min(low[v], low[u]) if low[u] > tin[v]: bridges.append((v, u))
for v in range(n): if not visited[v]: dfs(v, -1)
return bridgesArticulation Points (Cut Vertices)
Section titled “Articulation Points (Cut Vertices)”An articulation point is a vertex whose removal increases the number of connected components.
def find_articulation_points(n, adj): """ Find all articulation points in an undirected graph. Time: O(V + E) Space: O(V) """ visited = [False] * n tin = [0] * n low = [0] * n timer = [0] is_articulation = [False] * n
def dfs(v, parent): visited[v] = True tin[v] = low[v] = timer[0] timer[0] += 1 children = 0 for u in adj[v]: if u == parent: continue if visited[u]: low[v] = min(low[v], tin[u]) else: dfs(u, v) low[v] = min(low[v], low[u]) if low[u] >= tin[v] and parent != -1: is_articulation[v] = True children += 1 if parent == -1 and children > 1: is_articulation[v] = True
for v in range(n): if not visited[v]: dfs(v, -1)
return [v for v in range(n) if is_articulation[v]]Cross-References
Section titled “Cross-References”- Graph Algorithms — Dijkstra’s and MST algorithms provide the foundation for the advanced techniques here.
- Dynamic Programming — Floyd-Warshall is a classic all-pairs shortest path algorithm using dynamic programming.
- Binary Search Trees — Priority queues used in Dijkstra’s algorithm are implemented using heap-based trees.
- Deques and Priority Queues — Priority queues are essential for efficient implementations of shortest path algorithms.