🧠
Brute Force (Pure Recursion / Backtracking)
💡 This approach is the foundation for understanding how to explore all paths. It is simple but can be inefficient for large graphs due to repeated work and exponential path counts.
Intuition
Start from the source node and recursively explore all neighbors, building paths step-by-step until reaching the target node.
Algorithm
- Initialize a result list to store all paths.
- Define a recursive function that takes the current node and the path so far.
- If the current node is the target, add the path to the result.
- Otherwise, for each neighbor, recurse with the neighbor appended to the path.
💡 The recursion and backtracking can be tricky to visualize; it’s like exploring a tree of choices and returning when you hit a leaf.
from typing import List
def allPathsSourceTarget(graph: List[List[int]]) -> List[List[int]]:
res = []
target = len(graph) - 1
def dfs(node, path):
if node == target:
res.append(path[:])
return
for nei in graph[node]:
path.append(nei)
dfs(nei, path)
path.pop()
dfs(0, [0])
return res
# Driver code
if __name__ == '__main__':
graph = [[1,2],[3],[3],[]]
print(allPathsSourceTarget(graph))
Line Notes
res = []Initialize list to collect all valid paths
target = len(graph) - 1Identify the target node index
def dfs(node, path):Define recursive DFS helper to explore paths
if node == target:Base case: reached target, add current path copy to results
for nei in graph[node]:Explore all neighbors of current node
path.append(nei)Add neighbor to current path before recursion
dfs(nei, path)Recursive call to continue path exploration
path.pop()Backtrack by removing last node to explore other paths
dfs(0, [0])Start DFS from source node with initial path
return resReturn all collected paths after DFS completes
import java.util.*;
public class Solution {
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
List<List<Integer>> res = new ArrayList<>();
int target = graph.length - 1;
List<Integer> path = new ArrayList<>();
path.add(0);
dfs(0, target, graph, path, res);
return res;
}
private void dfs(int node, int target, int[][] graph, List<Integer> path, List<List<Integer>> res) {
if (node == target) {
res.add(new ArrayList<>(path));
return;
}
for (int nei : graph[node]) {
path.add(nei);
dfs(nei, target, graph, path, res);
path.remove(path.size() - 1);
}
}
// Driver code
public static void main(String[] args) {
Solution sol = new Solution();
int[][] graph = {{1,2},{3},{3},{}};
System.out.println(sol.allPathsSourceTarget(graph));
}
}
Line Notes
List<List<Integer>> res = new ArrayList<>();Initialize result list to store all paths
int target = graph.length - 1;Identify target node index
List<Integer> path = new ArrayList<>();Current path list to track nodes visited
path.add(0);Start path with source node
if (node == target)Base case: reached target node
res.add(new ArrayList<>(path));Add a copy of current path to results
for (int nei : graph[node])Iterate over neighbors of current node
path.add(nei);Add neighbor to path before recursion
dfs(nei, target, graph, path, res);Recursive DFS call
path.remove(path.size() - 1);Backtrack by removing last node
#include <iostream>
#include <vector>
using namespace std;
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(0, target, graph, path, res);
return res;
}
private:
void dfs(int node, int target, vector<vector<int>>& graph, 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(nei, target, graph, path, res);
path.pop_back();
}
}
};
// Driver code
int main() {
Solution sol;
vector<vector<int>> graph = {{1,2},{3},{3},{}};
vector<vector<int>> paths = sol.allPathsSourceTarget(graph);
for (auto &path : paths) {
for (int node : path) cout << node << ' ';
cout << '\n';
}
return 0;
}
Line Notes
vector<vector<int>> res;Stores all valid paths found
vector<int> path = {0};Initialize path with source node
int target = graph.size() - 1;Identify target node index
void dfs(int node, int target, vector<vector<int>>& graph, vector<int>& path, vector<vector<int>>& res)Recursive DFS helper function
if (node == target)Base case: reached target node
res.push_back(path);Add current path to results
for (int nei : graph[node])Iterate over neighbors of current node
path.push_back(nei);Add neighbor to path before recursion
dfs(nei, target, graph, path, res);Recursive DFS call
path.pop_back();Backtrack by removing last node
var allPathsSourceTarget = function(graph) {
const res = [];
const target = graph.length - 1;
function dfs(node, path) {
if (node === target) {
res.push([...path]);
return;
}
for (const nei of graph[node]) {
path.push(nei);
dfs(nei, path);
path.pop();
}
}
dfs(0, [0]);
return res;
};
// Driver code
const graph = [[1,2],[3],[3],[]];
console.log(allPathsSourceTarget(graph));
Line Notes
const res = [];Initialize array to store all paths
const target = graph.length - 1;Identify target node index
function dfs(node, path)Recursive DFS helper function
if (node === target)Base case: reached target node
res.push([...path]);Add a copy of current path to results
for (const nei of graph[node])Iterate over neighbors of current node
path.push(nei);Add neighbor to path before recursion
dfs(nei, path);Recursive DFS call
path.pop();Backtrack by removing last node
TimeO(2^n) in worst case due to exponential number of paths
SpaceO(n) recursion stack + O(2^n * n) for storing paths
Each node can branch to multiple neighbors, leading to exponential path counts; recursion stack depth is at most n.
💡 For n=20, this could mean millions of paths, making brute force impractical for large inputs.
Interview Verdict: Use only to introduce - not efficient for large inputs
This approach is easy to understand but too slow for big graphs; it helps build intuition.