加载中…
应用场景:连通性 · 层序 · 拓扑 · 路径搜索
交互式动画演示,可调整参数并单步执行。
全屏打开graph LR A[树/图] -->|DFS 栈| B[栈] A -->|BFS 队列| C[队列]
| 维度 | DFS(深度优先) | BFS(广度优先) |
|---|---|---|
| 数据结构 | 栈 / 递归 | 队列 |
| 顺序 | 一条路走到黑再回溯 | 按层扩散 |
| 最短路 | ❌ 不保证 | ✅ 无权图最短路 |
| 空间 | O(深度) | O(宽度) |
| 适用 | 连通块、拓扑序、环检测 | 最短路、层序、扩散 |
void dfs(TreeNode u) {
if (u == null) return;
// 前序位置:进入节点时
dfs(u.left);
// 中序位置
dfs(u.right);
// 后序位置:离开节点时(子树信息已齐全)
}核心思想:前序位置"自顶向下"传递参数(路径、深度),后序位置"自底向上"收集返回值。
void dfs(int u, boolean[] visited) {
visited[u] = true;
for (int v : graph[u]) {
if (!visited[v]) dfs(v, visited);
}
}Deque<int[]> stack = new ArrayDeque<>();
stack.push(new int[]{start, 0}); // {节点, 子节点遍历进度}
while (!stack.isEmpty()) {
int[] top = stack.peek();
int u = top[0], idx = top[1];
if (idx == graph[u].length) { // 子节点全部遍历完
stack.pop();
continue;
}
top[1]++;
int v = graph[u][idx];
if (!visited[v]) {
visited[v] = true;
stack.push(new int[]{v, 0});
}
}List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ans = new ArrayList<>();
if (root == null) return ans;
Deque<TreeNode> q = new ArrayDeque<>();
q.offer(root);
while (!q.isEmpty()) {
int sz = q.size();
List<Integer> level = new ArrayList<>(sz);
for (int i = 0; i < sz; i++) {
TreeNode u = q.poll();
level.add(u.val);
if (u.left != null) q.offer(u.left);
if (u.right != null) q.offer(u.right);
}
ans.add(level);
}
return ans;
}int[] dist = new int[n];
Arrays.fill(dist, -1);
Deque<Integer> q = new ArrayDeque<>();
q.offer(start); dist[start] = 0;
while (!q.isEmpty()) {
int u = q.poll();
for (int v : graph[u]) {
if (dist[v] == -1) {
dist[v] = dist[u] + 1;
q.offer(v);
}
}
}多个起点同时入队。例:矩阵中最短的 0-1 BFS、腐烂的橘子。
for (int i = 0; i < n; i++) if (grid[i] == 'O') q.offer(i);
// 之后正常 BFS起点和终点同时扩展。求 s → t 最短路:
// 适用:扩展规则对称、状态可哈希(如单词接龙、字母变换)
int bidirectionalBFS(String s, String t, Set<String> dict) {
if (s.equals(t)) return 0;
Set<String> head = new HashSet<>(Set.of(s));
Set<String> tail = new HashSet<>(Set.of(t));
Set<String> visited = new HashSet<>();
int step = 0;
while (!head.isEmpty() && !tail.isEmpty()) {
// 优化:每次扩展 size 较小的方向
if (head.size() > tail.size()) {
Set<String> tmp = head; head = tail; tail = tmp;
}
Set<String> next = new HashSet<>();
for (String cur : head) {
if (tail.contains(cur)) return step + 1; // 相遇
for (String nb : expand(cur, dict)) {
if (!visited.contains(nb)) {
visited.add(nb);
next.add(nb);
}
}
}
head = next;
step++;
}
return -1;
}复杂度从 O(b^d) 降到 O(b^(d/2))。前提:状态可哈希、扩展规则对称。
void backtrack(int[] nums, int start, List<Integer> path, List<List<Integer>> ans) {
ans.add(new ArrayList<>(path));
for (int i = start; i < nums.length; i++) {
path.add(nums[i]);
backtrack(nums, i + 1, path, ans);
path.remove(path.size() - 1);
}
}剪枝(pruning) 是回溯的灵魂:
if (i > start && nums[i] == nums[i-1]) continue;if (sum + rest < target) break;if (used > budget) return;int[] in = new int[n];
for (int[] e : edges) in[e[1]]++;
Deque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < n; i++) if (in[i] == 0) q.offer(i);
while (!q.isEmpty()) {
int u = q.poll(); order.add(u);
for (int v : graph[u]) if (--in[v] == 0) q.offer(v);
}
// 检测环:order.size() == n ? 无环 : 有环强连通分量、桥、割点是图论硬通货。基于 DFS 时间戳 + low 数组。
low[u] = min{
dfn[u],
dfn[v] for each back-edge (u, v),
low[w] for each tree-edge (u, w)
}
if (low[v] > dfn[u]) (u, v) is a bridge
if (low[v] >= dfn[u]) u is an articulation point (in some conditions)
边权为 0/1,用双端队列:权 0 加队首,权 1 加队尾。复杂度 O(V+E)。
int[] dist = new int[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[s] = 0;
// ⚠️ 不要用 (a, b) -> a[1] - b[1],会整数溢出
PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
pq.offer(new int[]{s, 0});
while (!pq.isEmpty()) {
int[] top = pq.poll();
int u = top[0], d = top[1];
if (d > dist[u]) continue; // 跳过过期条目
for (int[] e : graph[u]) {
int v = e[0], w = e[1];
if (dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
pq.offer(new int[]{v, dist[v]});
}
}
}dist[v] 存成 TreeSet<Integer> 即可。Dijkstra + 启发式估价 f = g + h。
要求 h 是可采纳的(不高于真实代价)。
例:八数码、地图导航。
| 题目特征 | 用什么 |
|---|---|
| 求最短路(无权图) | BFS |
| 求最短路(带正权) | Dijkstra |
| 求最短路(带负权) | Bellman-Ford / SPFA |
| 求连通分量 / 环 / 拓扑 | DFS / Kahn |
| 求树深 / 路径 | DFS(递归) |
| 求层序 / 最近距离 | BFS |
| 状态空间小、求最少步数 | BFS |
| 枚举所有解、子集 | DFS + 回溯 |
Math.max(depth, ...)。