Backtracking · 17 of 19
Print all possible paths from top left to bottom right of a mXn matrix
List every way to walk from the top left corner of a grid to the bottom right corner when you may only step right or down.
- Simple way
- time O(2^(m+n) * (m+n))
room O(m+n) - Clever way
- time O(C(m+n-2, m-1) * (m+n))
room O(m+n)
The problem, simply
You get a grid of numbers with m rows and n columns. Start in the top left cell and finish in the bottom right cell. Each step goes one cell right or one cell down.
Print every path, as the list of numbers you walk over.
A story to remember it
Picture it
A grid with 2 rows and 3 columns. Every path starts at 1 and ends at 6, and uses exactly 2 rights and 1 down.
- looking now
- answer
Examples
Two rows, three columns
- In
[[1, 2, 3], [4, 5, 6]]- Out
[1,2,3,6], [1,2,5,6], [1,4,5,6]
Choose where the one down step happens: after 1, after 2, or after 3.
A 3 by 3 grid
- In
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]- Out
[1,2,3,6,9], [1,2,5,6,9], [1,2,5,8,9], [1,4,5,6,9], [1,4,5,8,9], [1,4,7,8,9]
Four steps, two of them down. There are 6 ways to choose which two.
One row
- In
[[1, 2, 3]]- Out
[1, 2, 3]
You can only go right, so there is one path.
One cell
- In
[[7]]- Out
[7]
You start on the goal. The path is just that cell.
Way 1: the simple way
Every path has exactly m - 1 downs and n - 1 rights, so m + n - 2 steps in total. Write down every string of R and D of that length, and keep only those with the right number of D's.
- Count from
0to2^(m + n - 2) - 1. Read each number's bits as moves: 1 means down, 0 means right. - Skip the numbers that do not have exactly
m - 1ones. - For the rest, walk the moves from the top left and collect the cells.
import java.util.*;
class Solution {
public List<List<Integer>> allPaths(int[][] grid) {
int m = grid.length, n = grid[0].length, steps = m + n - 2;
List<List<Integer>> result = new ArrayList<>();
for (int mask = 0; mask < (1 << steps); mask++) {
if (Integer.bitCount(mask) != m - 1) continue; // must go down exactly m - 1 times
List<Integer> path = new ArrayList<>();
int r = 0, c = 0;
path.add(grid[0][0]);
for (int s = steps - 1; s >= 0; s--) { // first move is the highest bit
if ((mask >> s & 1) == 1) r++; else c++;
path.add(grid[r][c]);
}
result.add(path);
}
return result;
}
}How long it takes: we look at all 2^(m+n-2) move strings and walk each one in O(m + n), so O(2^(m+n) * (m+n)). Most strings are thrown away. Extra room: one path, O(m + n), not counting the answers.
Way 2: the clever way
Only ever make moves that stay inside the grid. Then every walk that reaches the corner is a real answer, and nothing is thrown away.
- Add the current cell to the path.
- If it is the bottom right cell, save a copy of the path.
- Otherwise, if there is a column to the right, go right. If there is a row below, go down.
- Remove the current cell from the path before returning. This is the step back.
Go right first, then down, and step back after each corner
01 / 10
Start at the top left cell, 1. From any cell you may go right (R) or down (D).
- path
- 1
- found
- 0
- looking now
- seen
- done
- answer
- path
import java.util.*;
class Solution {
public List<List<Integer>> allPaths(int[][] grid) {
List<List<Integer>> result = new ArrayList<>();
walk(grid, 0, 0, new ArrayList<>(), result);
return result;
}
private void walk(int[][] grid, int r, int c, List<Integer> path, List<List<Integer>> result) {
int m = grid.length, n = grid[0].length;
path.add(grid[r][c]);
if (r == m - 1 && c == n - 1) {
result.add(new ArrayList<>(path)); // reached the corner
} else {
if (c + 1 < n) walk(grid, r, c + 1, path, result); // right
if (r + 1 < m) walk(grid, r + 1, c, path, result); // down
}
path.remove(path.size() - 1); // step back
}
}How long it takes: the number of paths is C(m+n-2, m-1), the ways to choose which steps are downs, and each path has m + n - 1 cells to copy. So O(C(m+n-2, m-1) * (m+n)). That is exactly the size of the output, so no method can be faster. Extra room: the path and the call stack, O(m + n).
Watch out for
- Forgetting to remove the cell when stepping back. Paths then grow forever and contain cells from other paths.
- Checking
r < mbefore moving down instead ofr + 1 < m. You walk off the grid. - The number of paths explodes. A 10 by 10 grid has 48620 paths and a 16 by 16 grid has over 155 million. Ask about the size before printing.
What interviewers ask next
- Only count the paths. Use the formula
C(m+n-2, m-1), or a table where each cell adds the count from above and the count from the left,O(m * n). - Some cells are blocked. Skip moves into blocked cells. For counting, set the table to 0 on blocked cells.
- Find the path with the biggest sum. Use a table: each cell keeps its value plus the bigger of the best from above and from the left. No need to list every path.
- What if you may also go up and left? Then you can walk in circles, so you must mark cells on the current path and unmark them on the way back. That is the rat in a maze problem.
Remember this
Grown-up words: the grid is a matrix, the walks are paths in a grid graph, removing the cell on return is backtracking, and the number of paths is a binomial coefficient.