题目难度: 中等
今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复
剑指offer2
就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
给定一个有 n 个节点的有向无环图,用二维数组 graph 表示,请找到所有从 0 到 n-1 的路径并输出(不要求按顺序)。
graph 的第 i 个数组中的单元都表示有向图中 i 号节点所能到达的下一些节点(译者注:有向图是有方向的,即规定了 a→b 你就不能从 b→a ),若为空,就是没有下一个节点了。
示例 1:
- 输入:graph = [[1,2],[3],[3],[]]
- 输出:[[0,1,3],[0,2,3]]
- 解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
示例 2:
- 输入:graph = [[4,3,1],[3,2,4],[3],[4],[]]
- 输出:[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]
示例 3:
- 输入:graph = [[1],[]]
- 输出:[[0,1]]
示例 4:
- 输入:graph = [[1,2,3],[2],[3],[]]
- 输出:[[0,1,2,3],[0,2,3],[0,3]]
示例 5:
- 输入:graph = [[1,3],[2],[3],[]]
- 输出:[[0,1,2,3],[0,3]]
提示:
- n == graph.length
- 2 <= n <= 15
- 0 <= graph[i][j] < n
- graph[i][j] != i
- 保证输入为有向无环图 (GAD)
题目思考
- 如何利用题目给定的条件(有向无环图)?
解决方案
- 分析题目, 要求 0 到 n-1 的有效路径, 我们可以从 0 开始, 尝试其指向的每个节点作为下次遍历的节点, 并保存对应路径, 依此类推, 直到达到 n-1 为止
- 这就是典型递归回溯的思路, 具体步骤如下:
- 记录当前节点编号和路径, 初始时当前节点为 0, 且路径只包含它
- 遍历当前节点指向的下一个节点, 将它以及追加它之后的新路径作为新的参数进行递归调用
- 如果当前节点是 n-1, 则说明找到一条有效路径, 将其加入最终结果列表, 然后直接返回不再递归
- 注意经典的回溯需要使用 visit 集合来避免重复访问同一节点, 但由于题目限定了输入是有向无环图, 也就意味着同一条路径不可能重复访问同一节点, 所以这里就无需使用 visit 集合了
- 下面的代码对必要步骤有详细的解释, 方便大家理解
复杂度
- 时间复杂度
O(N*2^N)
: N 是节点总数, 最差情况下每个节点都指向所有大于它的节点, 此时有效路径的个数就是2^N
, 而每次递归调用的时间复杂度是O(N)
, 所以总时间复杂度就是O(N*2^N)
- 空间复杂度
O(N)
: 递归栈的消耗, 最坏情况下栈中需要保存所有 N 个节点
代码
class Solution:
def allPathsSourceTarget(self, graph: List[List[int]]) -> List[List[int]]:
### DFS+回溯
res = []
def dfs(cur, path):
if cur == len(graph) - 1:
# 当前节点是n-1, 找到了有效路径, 将其加入最终结果
res.append(path)
return
for nex in graph[cur]:
# 遍历当前节点指向的下一个节点, 并将其加入路径中
dfs(nex, path + [nex])
# 从节点0开始遍历, 此时路径只包含0
dfs(0, [0])
return res
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊