Unique Paths (LeetCode 62)
Picture a robot dropped onto the top-left cell of an m x n grid, aiming for the bottom-right cell. On every move it can step either one cell down or one cell right -- nothing else.
Given the grid dimensions m and n, count how many distinct routes the robot could take from start to finish. You can assume the true count never exceeds 2 * 109.
Worked Examples
m = 3, n = 7283 x 7 grid.m = 3, n = 23⚖️Formal Constraints & Bounds
1 <= m, n <= 100
Core Insight
Pascal's triangle addition achieves O(N) memory with one rolling row.
Real-World Scenario & Production Applications
Grid circuit routing paths, robotic path count enumeration.
Subproblems & Recurrence Decomposition3 Phases
Top row dp[0][j] and leftmost column dp[i][0] each have only 1 unique path (moving strictly right or strictly down).
# Perimeter Base Invariant
row = [1] * N # 1 path for every cell in top rowStep-by-Step Execution Trace Table
| Row | Column Values (dp array) |
|---|---|
| Row 0 | [1, 1, 1, 1, 1, 1, 1] |
| Row 1 | [1, 2, 3, 4, 5, 6, 7] |
| Row 2 | [1, 3, 6, 10, 15, 21, 28] |
The Math, Step by Step
1. The number the table holds
Number the rows from the top and the columns from the left. Call the number of different routes from the start cell to cell , moving only down or right. The answer is .
2. The value of one cell
The last move into cell comes from one of two cells.
| Where the robot was before | Last move | Routes that end this way |
|---|---|---|
| , the cell above | down | |
| , the cell to the left | right |
The two groups of routes do not overlap, because their last moves differ. Together they are all routes into . So
The first row and the first column are the base cases. A cell in row 0 can only be reached by moving right, so . A cell in column 0 can only be reached by moving down, so .
3. Why the table equals a binomial coefficient
Every route from to makes exactly down moves and right moves. That is moves in total. A route is fixed once you choose which of those moves are the down moves. The number of ways to choose positions out of is the binomial coefficient:
The recurrence of part 2 is the same rule that builds Pascal's triangle, . So the table the code fills is Pascal's triangle. The cells with the same value of form one row of the triangle. The code never computes a binomial coefficient. It only adds, and the formula confirms its result.
4. One row is enough
The code keeps a single list row of length instead of a full table. At the start, row is the first grid row, all .
For each next grid row, the inner loop runs row[j] += row[j - 1] for from 1 to . Before this line, row[j] still holds the value of the cell above, . row[j - 1] was already updated in this pass, so it holds the cell to the left, . The sum is .
row[0] is never changed, because column 0 always has route. The loop must run from left to right. If it ran from right to left, row[j - 1] would still hold the row above.
5. Example 1 by hand
m = 3, n = 7. The list row after each grid row:
| Grid row | row |
|---|---|
| 0 (start) | [1, 1, 1, 1, 1, 1, 1] |
| 1 | [1, 2, 3, 4, 5, 6, 7] |
| 2 | [1, 3, 6, 10, 15, 21, 28] |
The grid shows for every cell of the grid. The three marked cells show the rule of part 2 for the last cell. The cell above it holds , and the cell to its left holds . So the bottom-right cell holds .
The code returns row[6] = 28, the expected output. The formula agrees: .
6. Example 2 by hand
m = 3, n = 2. The list row starts as [1, 1]. After grid row 1 it is [1, 2], and after grid row 2 it is [1, 3]. The code returns 3, the expected output. The formula agrees: . The three routes are Right, Down, Down; Down, Down, Right; and Down, Right, Down.
7. From the formulas to the code
| Formula | In uniquePaths |
|---|---|
| for the first row | row = [1] * n |
| one pass per grid row | for _ in range(1, m): |
row[0] is never changed, because the inner loop starts at j = 1 | |
row[j] += row[j - 1] | |
return row[n - 1] |
The two loops run additions, so the code takes time. The list row takes space.
| 1 | row = [1] * n |
| 2 | for _ in range(1, m): |
| 3 | for j in range(1, n): |
| 4 | row[j] += row[j - 1] |
| 5 | return row[n - 1] |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
In a grid of size , a robot starts at the top-left corner and must navigate to the bottom-right corner , moving only Right or Down. Every path into cell must arrive from either the top neighbor or the left neighbor . By the Addition Principle of Combinatorics, the total unique paths into is . This generates Pascal's Triangle rotated across the grid.
🏟️ The Analogy: Two One-Way Traffic Rivers Merging
Imagine water flowing through an irrigation grid. Water is only allowed to flow East or South. At any intersection , the water volume arriving is simply the volume coming from the North plus the volume coming from the West. Along the top canal (row 0), water only flows East (1 path). Along the left canal (column 0), water only flows South (1 path). Every interior intersection simply sums the water volumes of its two feeding tributaries.
🔑 Why It Works
The space optimization is gorgeous: instead of allocating an matrix, we use a single 1D array of size . When computing row[j] += row[j - 1], the old value row[j] represents the cell directly above (from the previous row), while row[j - 1] is the newly updated cell to the left. The 2D matrix collapses into a 1D running accumulator!
Row/column boundary initialization off-by-one: Leaving outer rows/columns uninitialized. The robot can only reach any cell in the top row by moving right (1 way) and leftmost column by moving down (1 way). All boundary cells must be initialized to 1.
Allocating grid space unnecessarily: Calculating row-by-row requires only a single 1D rolling array of size :
dp[j] += dp[j - 1].Combinatorial factorial float precision drift: Calculating using floating-point division causes precision loss on large values of .
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
Counting paths in a rectangular grid moving only right and down.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: before row[j] += row[j - 1], row[j] holds the count for the cell above and row[j - 1] the count for the cell to the left, so after it row[j] is the count for the current cell.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
for j in range(1, n), notrange(0, n): atj = 0the linerow[j] += row[j - 1]readsrow[-1], the last cell, so the first column no longer has exactly one path.Obstacle grids (Unique Paths II (LC 63)): a blocked cell must be set to
0ways instead of summed, and a block in the first row or column cuts off every cell after it.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'd use Grid DP. A path can only enter a cell from above or from the left, so the number of paths to a cell is the paths to the cell above plus the paths to the cell on its left. I fill the grid row by row, left to right, so both of those are final when I read them, and one rolling row is enough. When I update
row[j], it still holds the cell above, androw[j - 1]already holds the cell to the left, so I add them. Every cell of the first row and the first column is reached exactly one way, sorowstarts as all ones andjstarts at 1. That is the trap: startingjat 0 readsrow[-1], the last cell, without an error, and five rows of a single column return 16 instead of 1. The answer isrow[n - 1], in O(M·N) time andO(N)space."
Complexity & Mathematical Proof
O(M · N)
Look at the execution steps: dp[c] = dp[c] + dp[c-1]. Fills M x N grid transitions using 1D rolling array of size N (columns) across M rows. Total M * N operations.
O(N) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. 1D rolling array of size N.
T(M, N) = M · N = O(M · N)
Derivation Progression
dp[r][c] = dp[r-1][c] + dp[r][c-1]
Unique paths to (r, c) equals sum of paths from top and left.
row[j] += row[j - 1]
Row-by-row updates compress space from O(M · N) to O(N).
M × N cell additions
Fills table in linear time relative to grid area.
Variable Definitions
Total number of rows in the grid
Total number of columns in the grid
Memory Architecture & Bounds
O(1) Iterative
O(N) 1D rolling array of size cols (or O(min(M, N)))
O(1) Unique paths integer
Boundary Best / Worst Cases
State-Space & Subproblem DAG(Directed Acyclic Graph) Topology
Senior SWE Deconstruction & Hardware Caveats
Triggers: "robot in m x n grid", "moves only down or right". 2D lattice path DP: .
. Time budget . Space budget: rolling 1D row array reduces memory to .
Scale: the count grows like a binomial coefficient, so on a square grid it passes the signed 32-bit limit at 18 × 18 and the signed 64-bit limit at 35 × 35 (LeetCode caps the answer at ; Python integers never overflow, Java and C++ ones do). For many queries on the same grid, fill the table once and answer each query by lookup instead of refilling it per query.
Unique Paths (LeetCode 62)
Picture a robot dropped onto the top-left cell of an m x n grid, aiming for the bottom-right cell. On every move it can step either one cell down or one cell right -- nothing else.
Given the grid dimensions m and n, count how many distinct routes the robot could take from start to finish. You can assume the true count never exceeds 2 * 109.
Worked Examples
m = 3, n = 7283 x 7 grid.m = 3, n = 23⚖️Formal Constraints & Bounds
1 <= m, n <= 100
Core Insight
Pascal's triangle addition achieves O(N) memory with one rolling row.
Real-World Scenario & Production Applications
Grid circuit routing paths, robotic path count enumeration.
Subproblems & Recurrence Decomposition3 Phases
Top row dp[0][j] and leftmost column dp[i][0] each have only 1 unique path (moving strictly right or strictly down).
# Perimeter Base Invariant
row = [1] * N # 1 path for every cell in top rowStep-by-Step Execution Trace Table
| Row | Column Values (dp array) |
|---|---|
| Row 0 | [1, 1, 1, 1, 1, 1, 1] |
| Row 1 | [1, 2, 3, 4, 5, 6, 7] |
| Row 2 | [1, 3, 6, 10, 15, 21, 28] |
The Math, Step by Step
1. The number the table holds
Number the rows from the top and the columns from the left. Call the number of different routes from the start cell to cell , moving only down or right. The answer is .
2. The value of one cell
The last move into cell comes from one of two cells.
| Where the robot was before | Last move | Routes that end this way |
|---|---|---|
| , the cell above | down | |
| , the cell to the left | right |
The two groups of routes do not overlap, because their last moves differ. Together they are all routes into . So
The first row and the first column are the base cases. A cell in row 0 can only be reached by moving right, so . A cell in column 0 can only be reached by moving down, so .
3. Why the table equals a binomial coefficient
Every route from to makes exactly down moves and right moves. That is moves in total. A route is fixed once you choose which of those moves are the down moves. The number of ways to choose positions out of is the binomial coefficient:
The recurrence of part 2 is the same rule that builds Pascal's triangle, . So the table the code fills is Pascal's triangle. The cells with the same value of form one row of the triangle. The code never computes a binomial coefficient. It only adds, and the formula confirms its result.
4. One row is enough
The code keeps a single list row of length instead of a full table. At the start, row is the first grid row, all .
For each next grid row, the inner loop runs row[j] += row[j - 1] for from 1 to . Before this line, row[j] still holds the value of the cell above, . row[j - 1] was already updated in this pass, so it holds the cell to the left, . The sum is .
row[0] is never changed, because column 0 always has route. The loop must run from left to right. If it ran from right to left, row[j - 1] would still hold the row above.
5. Example 1 by hand
m = 3, n = 7. The list row after each grid row:
| Grid row | row |
|---|---|
| 0 (start) | [1, 1, 1, 1, 1, 1, 1] |
| 1 | [1, 2, 3, 4, 5, 6, 7] |
| 2 | [1, 3, 6, 10, 15, 21, 28] |
The grid shows for every cell of the grid. The three marked cells show the rule of part 2 for the last cell. The cell above it holds , and the cell to its left holds . So the bottom-right cell holds .
The code returns row[6] = 28, the expected output. The formula agrees: .
6. Example 2 by hand
m = 3, n = 2. The list row starts as [1, 1]. After grid row 1 it is [1, 2], and after grid row 2 it is [1, 3]. The code returns 3, the expected output. The formula agrees: . The three routes are Right, Down, Down; Down, Down, Right; and Down, Right, Down.
7. From the formulas to the code
| Formula | In uniquePaths |
|---|---|
| for the first row | row = [1] * n |
| one pass per grid row | for _ in range(1, m): |
row[0] is never changed, because the inner loop starts at j = 1 | |
row[j] += row[j - 1] | |
return row[n - 1] |
The two loops run additions, so the code takes time. The list row takes space.
| 1 | row = [1] * n |
| 2 | for _ in range(1, m): |
| 3 | for j in range(1, n): |
| 4 | row[j] += row[j - 1] |
| 5 | return row[n - 1] |
Conceptual Narrative
🧭 Conceptual Foundation & Pattern Intuition
In a grid of size , a robot starts at the top-left corner and must navigate to the bottom-right corner , moving only Right or Down. Every path into cell must arrive from either the top neighbor or the left neighbor . By the Addition Principle of Combinatorics, the total unique paths into is . This generates Pascal's Triangle rotated across the grid.
🏟️ The Analogy: Two One-Way Traffic Rivers Merging
Imagine water flowing through an irrigation grid. Water is only allowed to flow East or South. At any intersection , the water volume arriving is simply the volume coming from the North plus the volume coming from the West. Along the top canal (row 0), water only flows East (1 path). Along the left canal (column 0), water only flows South (1 path). Every interior intersection simply sums the water volumes of its two feeding tributaries.
🔑 Why It Works
The space optimization is gorgeous: instead of allocating an matrix, we use a single 1D array of size . When computing row[j] += row[j - 1], the old value row[j] represents the cell directly above (from the previous row), while row[j - 1] is the newly updated cell to the left. The 2D matrix collapses into a 1D running accumulator!
Row/column boundary initialization off-by-one: Leaving outer rows/columns uninitialized. The robot can only reach any cell in the top row by moving right (1 way) and leftmost column by moving down (1 way). All boundary cells must be initialized to 1.
Allocating grid space unnecessarily: Calculating row-by-row requires only a single 1D rolling array of size :
dp[j] += dp[j - 1].Combinatorial factorial float precision drift: Calculating using floating-point division causes precision loss on large values of .
4-Phase Thought Process Model
Pattern Recognition Signals
The 10-second spot
Counting paths in a rectangular grid moving only right and down.
Formulating the Predicate & Invariants
Turning intuition into a boolean rule
Invariant: before row[j] += row[j - 1], row[j] holds the count for the cell above and row[j - 1] the count for the cell to the left, so after it row[j] is the count for the current cell.
Silent Failure Traps & Edge Cases
Where confident candidates still lose points
for j in range(1, n), notrange(0, n): atj = 0the linerow[j] += row[j - 1]readsrow[-1], the last cell, so the first column no longer has exactly one path.Obstacle grids (Unique Paths II (LC 63)): a blocked cell must be set to
0ways instead of summed, and a block in the first row or column cuts off every cell after it.
The 60-Second Interview Pitch
Say this out loud before you type a single line
"I'd use Grid DP. A path can only enter a cell from above or from the left, so the number of paths to a cell is the paths to the cell above plus the paths to the cell on its left. I fill the grid row by row, left to right, so both of those are final when I read them, and one rolling row is enough. When I update
row[j], it still holds the cell above, androw[j - 1]already holds the cell to the left, so I add them. Every cell of the first row and the first column is reached exactly one way, sorowstarts as all ones andjstarts at 1. That is the trap: startingjat 0 readsrow[-1], the last cell, without an error, and five rows of a single column return 16 instead of 1. The answer isrow[n - 1], in O(M·N) time andO(N)space."
Complexity & Mathematical Proof
O(M · N)
Look at the execution steps: dp[c] = dp[c] + dp[c-1]. Fills M x N grid transitions using 1D rolling array of size N (columns) across M rows. Total M * N operations.
O(N) Auxiliary
Look at memory allocations: Call stack uses O(1) Iterative. 1D rolling array of size N.
T(M, N) = M · N = O(M · N)
Derivation Progression
dp[r][c] = dp[r-1][c] + dp[r][c-1]
Unique paths to (r, c) equals sum of paths from top and left.
row[j] += row[j - 1]
Row-by-row updates compress space from O(M · N) to O(N).
M × N cell additions
Fills table in linear time relative to grid area.
Variable Definitions
Total number of rows in the grid
Total number of columns in the grid
Memory Architecture & Bounds
O(1) Iterative
O(N) 1D rolling array of size cols (or O(min(M, N)))
O(1) Unique paths integer
Boundary Best / Worst Cases
State-Space & Subproblem DAG(Directed Acyclic Graph) Topology
Senior SWE Deconstruction & Hardware Caveats
Triggers: "robot in m x n grid", "moves only down or right". 2D lattice path DP: .
. Time budget . Space budget: rolling 1D row array reduces memory to .
Scale: the count grows like a binomial coefficient, so on a square grid it passes the signed 32-bit limit at 18 × 18 and the signed 64-bit limit at 35 × 35 (LeetCode caps the answer at ; Python integers never overflow, Java and C++ ones do). For many queries on the same grid, fill the table once and answer each query by lookup instead of refilling it per query.