题目难度: 中等

原题链接

今天继续更新 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)

题目思考

  1. 如何利用题目给定的条件(有向无环图)?

解决方案

  • 分析题目, 要求 0 到 n-1 的有效路径, 我们可以从 0 开始, 尝试其指向的每个节点作为下次遍历的节点, 并保存对应路径, 依此类推, 直到达到 n-1 为止
  • 这就是典型递归回溯的思路, 具体步骤如下:
    1. 记录当前节点编号和路径, 初始时当前节点为 0, 且路径只包含它
    2. 遍历当前节点指向的下一个节点, 将它以及追加它之后的新路径作为新的参数进行递归调用
    3. 如果当前节点是 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

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我