All Paths From Source to Target

MediumGraph DFS · Backtracking🧩 Graph DFSLeetCode #797 ↗Updated

Problem

A directed acyclic graph with nodes 0..n-1 is given as an adjacency list: graph[i] lists the nodes reachable from i by one edge. Return every path that starts at node 0 and ends at node n - 1, each as the list of visited nodes. The paths may be returned in any order.
Inputgraph = [[1,2],[3],[3],[]]Output[[0,1,3],[0,2,3]]
Inputgraph = [[4,3,1],[3,2,4],[3],[4],[]]Output[[0,4],[0,3,4],[0,1,3,4],[0,1,2,3,4],[0,1,4]]

2 ≤ n ≤ 15  ·  graph[i] has distinct values, no self-loops  ·  the graph is a DAG

Try it yourself

Java
class Solution {
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        
    }
}
Your code is saved in this browser.Submit on LeetCode ↗
Key insight
Because the graph has no cycles, no visited set is needed: any walk from 0 is automatically a simple path. Grow one shared path with DFS, snapshot it when you hit n - 1, and undo the last step on the way back.
PatternGraph DFS

Instinct Connected regions, all paths, reachability, cloning or 2-colouring a graph or grid: dive from a node, mark it, recurse into unvisited neighbours.

Memory hack Mark before you march: go deep, paint as you go, and back up only when every door is already painted.

Open the Graph DFS guide →

Approaches

Brute ForceBFS over Partial Paths

Time O(2n · n)Space O(2n · n)

Put the path [0] in a queue. Repeatedly take a path, and for each neighbour of its last node create a copy extended by that neighbour; copies ending at n - 1 are answers, the rest go back into the queue. Since the graph is acyclic, the process stops.

Java
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
    int target = graph.length - 1;
    List<List<Integer>> res = new ArrayList<>();
    Queue<List<Integer>> queue = new ArrayDeque<>();
    queue.offer(new ArrayList<>(List.of(0)));
    while (!queue.isEmpty()) {
        List<Integer> path = queue.poll();
        int last = path.get(path.size() - 1);
        for (int next : graph[last]) {
            List<Integer> extended = new ArrayList<>(path); // copy per edge
            extended.add(next);
            if (next == target) res.add(extended);
            else queue.offer(extended);
        }
    }
    return res;
}

Correct and iterative, but every extension copies a path and a whole frontier of paths sits in memory at once.

At a glance

LevelApproachTimeSpace
Brute ForceBFS over Partial PathsO(2n · n)O(2n · n)
OptimalDFS Backtracking with One Shared PathO(2n · n)O(n) + output
Common pitfalls
  • Adding path itself to the result instead of a copy — every entry ends up empty.
  • Forgetting to remove the last node after the recursive call (no backtrack).
  • Starting with an empty path — node 0 must be the first element.
Interview tips
  • Point out that output can be exponential, so no algorithm beats O(2n·n).
  • Explain why the DAG property removes the need for a visited set.
  • Compare memory: BFS stores a frontier of full paths, DFS stores one path.
Pattern
DFS + backtracking on a DAG → enumerate all source-to-sink paths or combinations along edges.
My notes saved in this browser