已發布·持續改進
圖形走訪 指南 · 3/6
本章目前僅提供英文版。
This chapter implements BFS, DFS and Kahn's topological sort in Python on an adjacency list adj (a list of lists) whose vertices are numbered 0 to n-1. It then ports the same BFS to C++, Java and TypeScript.
from collections import deque
def bfs(adj, start):
n = len(adj)
dist = [-1] * n # -1 = not reached yet
parent = [-1] * n
dist[start] = 0
queue = deque([start])
while queue:
u = queue.popleft()
for v in adj[u]:
if dist[v] == -1: # first time we see v
dist[v] = dist[u] + 1
parent[v] = u
queue.append(v)
return dist, parent
def path_to(dist, parent, target):
if dist[target] == -1:
return None # unreachable
path = []
while target != -1:
path.append(target)
target = parent[target]
return path[::-1]dist doubles as the visited marker: anything other than -1 has already been queued.deque.popleft() is O(1). list.pop(0) is O(n) and must not be used as a queue.path_to walks the parent pointers back from the target and reverses the result, returning None for unreachable vertices.def dfs_recursive(adj, start):
seen = [False] * len(adj)
order = []
def visit(u):
seen[u] = True
order.append(u)
for v in adj[u]:
if not seen[v]:
visit(v)
visit(start)
return order
def count_components(adj):
comp = [-1] * len(adj)
count = 0
for s in range(len(adj)):
if comp[s] != -1:
continue
comp[s] = count
stack = [s]
while stack: # iterative DFS
u = stack.pop()
for v in adj[u]:
if comp[v] == -1:
comp[v] = count
stack.append(v)
count += 1
return count, compcount_components starts a new traversal from every vertex that has no label yet; each traversal paints one component. Only reachability matters here, not order, so a simple mark-on-push stack is enough.def topo_kahn(n, edges):
adj = [[] for _ in range(n)]
indeg = [0] * n
for u, v in edges:
adj[u].append(v)
indeg[v] += 1
queue = deque(i for i in range(n) if indeg[i] == 0)
order = []
while queue:
u = queue.popleft()
order.append(u)
for v in adj[u]:
indeg[v] -= 1
if indeg[v] == 0:
queue.append(v)
return order if len(order) == n else None # None = cycle
WHITE, GRAY, BLACK = 0, 1, 2
def topo_dfs(adj):
color = [WHITE] * len(adj)
order = []
def visit(u):
color[u] = GRAY # in progress
for v in adj[u]:
if color[v] == GRAY:
raise ValueError("cycle") # back edge
if color[v] == WHITE:
visit(v)
color[u] = BLACK # finished
order.append(u)
for u in range(len(adj)):
if color[u] == WHITE:
visit(u)
return order[::-1]C++ uses std::queue.
#include <queue>
#include <vector>
std::vector<int> bfs(const std::vector<std::vector<int>>& adj, int start) {
std::vector<int> dist(adj.size(), -1);
std::queue<int> q;
dist[start] = 0;
q.push(start);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int v : adj[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
q.push(v);
}
}
}
return dist;
}In Java, ArrayDeque is the fastest general-purpose queue.
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.List;
final class Graphs {
static int[] bfs(List<List<Integer>> adj, int start) {
int[] dist = new int[adj.size()];
Arrays.fill(dist, -1);
ArrayDeque<Integer> queue = new ArrayDeque<>();
dist[start] = 0;
queue.add(start);
while (!queue.isEmpty()) {
int u = queue.poll();
for (int v : adj.get(u)) {
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
queue.add(v);
}
}
}
return dist;
}
}Array.prototype.shift() is O(n) in TypeScript, so the queue is simulated with a moving head index.
export function bfs(adj: number[][], start: number): number[] {
const dist = new Array<number>(adj.length).fill(-1);
const queue: number[] = [start];
dist[start] = 0;
for (let head = 0; head < queue.length; head++) {
const u = queue[head];
for (const v of adj[u]) {
if (dist[v] === -1) {
dist[v] = dist[u] + 1;
queue.push(v);
}
}
}
return dist;
}BFS needs only a deque and a distance array for shortest distances, plus a parent array for paths. Recursive DFS is concise, but the iterative form is safer on deep graphs, and components are counted with one traversal per unlabeled vertex. Both topological sorts, Kahn (in-degrees) and DFS (reversed finish order), detect cycles as a side effect. When porting, the key is a queue with O(1) operations in each language.
0 則留言
登入 · 登入後即可留言。
來留下第一則留言吧。