leetcode797. 所有可能的路径(java)

265 篇文章 2 订阅
235 篇文章 0 订阅

所有可能的路径

题目描述

难度 - 中等
leetcode797. 所有可能的路径

给你一个有 n 个节点的 有向无环图(DAG),请你找出所有从节点 0 到节点 n-1 的路径并输出(不要求按特定顺序)
graph[i] 是一个从节点 i 可以访问的所有节点的列表(即从节点 i 到节点 graph[i][j]存在一条有向边)。

示例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]]

提示:
n == graph.length
2 <= n <= 15
0 <= graph[i][j] < n
graph[i][j] != i(即不存在自环)
graph[i] 中的所有元素 互不相同
保证输入为 有向无环图(DAG)

在这里插入图片描述

图的遍历

图怎么遍历?还是那句话,参考多叉树,多叉树的遍历框架如下:

/* 多叉树遍历框架 */
void traverse(TreeNode root) {
    if (root == null) return;

    for (TreeNode child : root.children) {
        traverse(child);
    }
}

图和多叉树最大的区别是,图是可能包含环的,你从图的某一个节点开始遍历,有可能走了一圈又回到这个节点。
所以,如果图包含环,遍历框架就要一个visited数组进行辅助:

// 记录被遍历过的节点
boolean[] visited;
// 记录从起点到当前节点的路径
boolean[] onPath;

/* 图遍历框架 */
void traverse(Graph graph, int s) {
    if (visited[s]) return;
    // 经过节点 s,标记为已遍历
    visited[s] = true;
    // 做选择:标记节点 s 在路径上
    onPath[s] = true;
    for (int neighbor : graph.neighbors(s)) {
        traverse(graph, neighbor);
    }
    // 撤销选择:节点 s 离开路径
    onPath[s] = false;
}

注意visited数组和onPath数组的区别,因为二叉树算是特殊的图,所以用遍历二叉树的过程来理解下这两个数组的区别:
在这里插入图片描述>上述 GIF 描述了递归遍历二叉树的过程,在visited中被标记为 true 的节点用灰色表示,在onPath中被标记为 true 的节点用绿色表示,这下你可以理解它们二者的区别了吧。
如果让你处理路径相关的问题,这个onPath变量是肯定会被用到的,比如 拓扑排序 中就有运用。
另外,你应该注意到了,这个onPath数组的操作很像 回溯算法核心套路 中做「做选择」和「撤销选择」,区别在于位置:回溯算法的「做选择」和「撤销选择」在 for 循环里面,而对onPath数组的操作在 for 循环外面。
在 for 循环里面和外面唯一的区别就是对根节点的处理。
比如下面两种多叉树的遍历:

void traverse(TreeNode root) {
    if (root == null) return;
    System.out.println("enter: " + root.val);
    for (TreeNode child : root.children) {
        traverse(child);
    }
    System.out.println("leave: " + root.val);
}

void traverse(TreeNode root) {
    if (root == null) return;
    for (TreeNode child : root.children) {
        System.out.println("enter: " + child.val);
        traverse(child);
        System.out.println("leave: " + child.val);
    }
}

前者会正确打印所有节点的进入和离开信息,而后者唯独会少打印整棵树根节点的进入和离开信息。

这个问题可以直接套用上面框架,因为,是无环的,所以不需要visited标记。

代码演示

class Solution {
    List<List<Integer>> ans = new LinkedList<>();
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        LinkedList<Integer> path = new LinkedList<>();
        transe(graph,0,path);
        return ans;
    }
    public void transe(int[][]graph,int index,LinkedList<Integer>path){
      path.addLast(index);
      int n = graph.length;
      //到达终点
      if(index == n - 1){
          ans.add(new LinkedList<>(path));
          path.removeLast();
          return;
      }
      for(int x : graph[index]){
          transe(graph,x,path);
      }
      //移除index 
      path.removeLast();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值