class Solution:
def allPathsSourceTarget(self, graph):
res = []
path = [0]
target = len(graph) - 1
def dfs(node):
if node == target:
res.append(list(path))
return
for nei in graph[node]:
path.append(nei)
dfs(nei)
path.pop()
dfs(0)
return res
class Solution {
public:
vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
vector<vector<int>> res;
vector<int> path = {0};
int target = graph.size() - 1;
dfs(graph, 0, target, path, res);
return res;
}
void dfs(vector<vector<int>>& graph, int node, int target, vector<int>& path, vector<vector<int>>& res) {
if (node == target) {
res.push_back(path);
return;
}
for (int nei : graph[node]) {
path.push_back(nei);
dfs(graph, nei, target, path, res);
path.pop_back();
}
}
};
class Solution {
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
path.add(0);
dfs(graph, 0, path, res);
return res;
}
private void dfs(int[][] graph, int node, List<Integer> path, List<List<Integer>> res) {
if (node == graph.length - 1) {
res.add(new ArrayList<>(path));
return;
}
for (int nei : graph[node]) {
path.add(nei);
dfs(graph, nei, path, res);
path.remove(path.size() - 1);
}
}
}
var allPathsSourceTarget = function(graph) {
const res = [];
const path = [0];
const target = graph.length - 1;
function dfs(node) {
if (node === target) {
res.push([...path]);
return;
}
for (let nei of graph[node]) {
path.push(nei);
dfs(nei);
path.pop();
}
}
dfs(0);
return res;
};
Given a directed acyclic graph (DAG) represented as an adjacency list, your task is to find all possible paths from the source node (node 0
) to the target node (node n - 1
), where n
is the number of nodes in the graph.
The input is a list of lists, where graph[i]
contains all nodes that node i
points to directly. You must return a list of all valid paths from the source to the target, with each path represented as a list of node indices.
Key constraints:
0
and end at node n - 1
.
The challenge is to systematically explore all possible ways to get from the source to the target in a directed acyclic graph. At first glance, it might seem like we could simply "walk" from node 0
to node n - 1
, but since there may be multiple choices at each node, we need to consider every combination.
A brute-force approach would mean trying every possible route, backtracking when we reach a dead end or reach the target. This naturally leads us to consider Depth-First Search (DFS), where we "dive" down each possible path, and back up to try alternatives.
Since the graph is acyclic, we don't need to worry about cycles or infinite loops. Also, since we want all paths, we can't stop at the first solution; we must collect every valid route.
We can solve this problem using a recursive DFS (Depth-First Search) approach. Here’s how:
n - 1
), we add a copy of the current path to our results.
Why use DFS? DFS is ideal because it naturally follows each path to the end before trying alternatives. Since the graph has no cycles, we don’t need to keep a visited set for this problem.
Let’s use the sample input graph = [[1,2], [3], [3], []]
. This means:
Here’s how the DFS would unfold:
[[0,1,3],[0,2,3]]
.
Brute-force approach: In the worst case, where every node connects to every other node, the number of possible paths from source to target could be exponential in the number of nodes, i.e., up to O(2n) for n
nodes.
Optimized approach (DFS):
n
, and there can be up to O(2n) paths.To solve "All Paths From Source to Target," we use depth-first search to explore every possible path from the source to the target in the directed acyclic graph. DFS is well-suited for this task because it allows us to build up each path step by step and backtrack efficiently. The key insight is that, since the graph is acyclic, we don’t need to worry about cycles, and we can safely use recursion and backtracking to enumerate all valid paths. The solution is elegant in its simplicity and leverages the natural structure of DFS for pathfinding in graphs.