LeetCode 797. 所有可能的路径
题目描述



题意分析
给定有向无环图,
graph[i]列出节点i的所有出边终点。要求返回从 0 到n-1的全部路径,顺序不限。不同路径可以在中途汇合并共用后续节点,因此要保留每条路径的完整前缀。
解法:DFS 回溯构造全部路径
核心思路
[!blue]
用
path保存从 0 到当前节点node的完整路径,dfs(node)尝试把它继续延长。对graph[node]中的每个邻居,先把邻居加入路径,再递归搜索,返回时删除刚加入的节点,恢复本层原来的路径。到达
n-1时,当前路径已经是一条答案,复制保存后返回。必须保存副本,因为工作路径随后还会被其他分支追加和撤销;如果保存同一个列表或切片,它的内容可能随着回溯发生变化。每一步都沿真实出边扩展,所以收集到的路径一定合法;任意一条从起点到终点的路径,也都对应搜索中依次选择这些出边的分支,因此不会遗漏。无环保证一条路径不会重复经过节点,深度最多为节点数,搜索一定结束。
这里不能用永久的全局
visited跳过已经访问过的节点。同一个节点从不同前缀到达时,会形成不同的完整路径,必须分别继续搜索;图本身无环,已经提供了避免无限递归的保证。
解题步骤
- 把 0 加入空路径,调用
dfs(0)。- 若当前节点等于
n-1,将路径副本加入结果并返回。- 否则遍历所有出边,对每个邻居依次执行加入路径、递归、移出路径。
- 搜索结束返回结果列表;如果起点无法到达终点,结果为空。
没有出边的节点不一定是指定终点。到达其他死路时,循环自然结束并回退,不能把这种未完成路径加入答案。
代码实现
class Solution {
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
path.add(0);
dfs(0, graph, path, res);
return res;
}
private void dfs(int node, int[][] graph, List<Integer> path, List<List<Integer>> res) {
if (node == graph.length - 1) {
// 复制当前路径,兄弟分支会继续修改工作缓冲
res.add(new ArrayList<>(path));
return;
}
for (int next : graph[node]) {
path.add(next);
dfs(next, graph, path, res);
// 分支结束撤销邻居,保留父层原有路径
path.remove(path.size() - 1);
}
}
}
func allPathsSourceTarget(graph [][]int) [][]int {
res := make([][]int, 0)
path := []int{
0,
}
var dfs func(node int)
dfs = func(node int) {
if node == len(graph)-1 {
copyPath := make([]int, len(path))
// 复制当前路径,兄弟分支会继续修改工作缓冲
copy(copyPath, path)
res = append(res, copyPath)
return
}
for _, next := range graph[node] {
path = append(path, next)
dfs(next)
// 分支结束撤销邻居,保留父层原有路径
path = path[:len(path)-1]
}
}
dfs(0)
return res
}
复杂度分析
- 时间复杂度:最坏为 $O(n2^n)$。DAG 中的路径数量可能达到指数级,每条答案还需要复制最多 $n$ 个节点,因此不能按普通“每个节点只访问一次”的 DFS 估算。
- 空间复杂度:不计输出为 $O(n)$,来自当前路径和递归栈;输出本身最坏占 $O(n2^n)$。
关键点总结
[!green]
- 叶子不一定是指定终点,必须按编号判断。
- 结果保存副本,工作路径继续回溯。
易错点总结
[!yellow]
- 保存路径引用会被后续撤销改变。
- 进入邻居前不加入路径,会漏掉节点。
- 永久标记汇合节点,会误剪其他合法路径。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 126. 单词接龙 II | 困难 | 同样输出从起点到终点的多条路径,原题只要最短路径,本题DAG中所有路径都要保留。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!