3.所有可能路径
- 题目描述
给你一个有 n
个节点的 有向无环图(DAG) ,请你找出所有从节点 0
到节点 n-1
的路径并输出(不要求按特定顺序)
graph[i]
是一个从节点 i
可以访问的所有节点的列表(即从节点 i
到节点 graph[i][j]
存在一条有向边)。
示例 1:
java
输入:graph = [[1,2],[3],[3],[]]
输出:[[0,1,3],[0,2,3]]
解释:有两条路径 0 -> 1 -> 3 和 0 -> 2 -> 3
- 题目分析
java
具体解题思路如下:
创建一个数组 used 用于标记节点是否被遍历过,初始化为0。
将源节点 0 加入初始路径,并调用 dfs 函数开始深度优先搜索。
在 dfs 函数中,首先判断当前节点是否为目标节点,如果是,则将当前路径添加到结果中,并标记当前节点未被访问过,然后返回。
如果当前节点不是目标节点,遍历当前节点可到达的所有节点,如果该节点未被访问过,则将节点加入当前路径,以该节点为起点继续递归调用 dfs 函数。在递归调用结束后,需要进行回溯操作,即移除最后一个节点,尝试其他路径。
这样就可以找出所有从源节点到目标节点的路径,并将其存储在 result 中。
- Java代码分析
深度优先遍历(使用used数组)
java
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
public class Solution {
// 用于存储当前路径的节点
LinkedList<Integer> path = new LinkedList<>();
// 存储所有符合条件的路径
List<List<Integer>> result = new ArrayList<>();
// 主函数,找出从源节点到目标节点的所有路径
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
// 创建一个数组,用于标记节点是否被遍历
int[] used = new int[graph.length];
path.add(0); // 将源节点加入初始路径
dfs(graph, used, 0); // 开始深度优先搜索
return result; // 返回所有路径
}
// 深度优先搜索函数
private void dfs(int[][] graph, int[] used, int startNode) {
// 如果当前节点为目标节点,将当前路径添加到结果中
if (startNode == graph.length - 1) {
result.add(new ArrayList<>(path));
used[startNode] = 0; // 标记当前节点未被访问
return;
}
// 遍历当前节点可到达的所有节点
for (int i = 0; i < graph[startNode].length; i++) {
// 如果该节点未被访问过,继续深度优先搜索
if (used[graph[startNode][i]] == 0) {
path.add(graph[startNode][i]); // 将节点加入当前路径
dfs(graph, used, graph[startNode][i]); // 以该节点为起点继续搜索
path.removeLast(); // 回溯,移除最后一个节点,尝试其他路径
}
}
}
}
为什么此题可以不使用used数组?
md
这是因为在深度优先搜索中,我们使用了路径 path 来记录当前的访问状态,每次递归调用都会将当前节点加入路径,并在递归结束后将其移出路径。这样就不需要额外的 used 数组来标记节点是否被访问过,因为路径 path 本身已经隐式地记录了节点的访问状态。
当我们尝试访问一个节点时,首先会检查该节点是否已经在当前路径中,如果在,则说明形成了环路,不再继续访问;如果不在,则将该节点加入路径,并继续向下递归。在每一次递归结束后,我们会将最后一个节点从路径中移出,这样就能够正确地模拟节点的访问状态,而不需要额外的 used 数组来记录。
java
import java.util.LinkedList;
import java.util.List;
import java.util.ArrayList;
public class AllPathsSourceTarget {
LinkedList<Integer> path = new LinkedList<>(); // 用于存储当前路径的节点序列
List<List<Integer>> result = new ArrayList<>(); // 存储所有从起点到终点的路径
// 主方法,返回所有从起点到终点的路径
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
path.add(0); // 将起点添加到路径中
dfs(graph, 0); // 开始深度优先搜索
return result; // 返回所有路径结果
}
// 深度优先搜索方法
private void dfs(int[][] graph, int x) {
// 判断是否到达终点
if (x == graph.length - 1) {
result.add(new ArrayList<>(path)); // 将当前路径添加到结果中
return;
}
// 遍历当前节点的邻居节点
for (int i = 0; i < graph[x].length; i++) {
int node = graph[x][i]; // 获取邻居节点
path.add(node); // 将邻居节点加入路径中
dfs(graph, node); // 递归搜索邻居节点
path.removeLast(); // 回溯,移除当前节点,继续搜索其他邻居
}
}
}