Unique Paths in Grid DP in DSA Go - Time & Space Complexity
We want to know how the time needed to find unique paths in a grid grows as the grid size increases.
How does the number of steps change when the grid gets bigger?
Analyze the time complexity of the following code snippet.
func uniquePaths(m int, n int) int {
dp := make([][]int, m)
for i := 0; i < m; i++ {
dp[i] = make([]int, n)
dp[i][0] = 1
}
for j := 0; j < n; j++ {
dp[0][j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
}
This code calculates the number of unique paths from the top-left to the bottom-right of an m by n grid using dynamic programming.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Nested loops filling the dp table.
- How many times: The inner loop runs (m-1) * (n-1) times, covering almost all cells except the first row and column.
As the grid size grows, the number of operations grows roughly with the number of cells in the grid.
| Input Size (m x n) | Approx. Operations |
|---|---|
| 10 x 10 | 81 (9*9) |
| 100 x 100 | 9,801 (99*99) |
| 1000 x 1000 | 998,001 (999*999) |
Pattern observation: Operations grow roughly as the product of m and n, so doubling both roughly quadruples the work.
Time Complexity: O(m * n)
This means the time needed grows proportionally to the number of cells in the grid.
[X] Wrong: "The time grows exponentially because there are many paths."
[OK] Correct: The dynamic programming approach calculates each cell once, so it avoids repeated work and runs in polynomial time, not exponential.
Understanding how nested loops affect time helps you explain and optimize grid problems clearly in interviews.
"What if we used recursion without memoization? How would the time complexity change?"
Practice
Unique Paths means:Solution
Step 1: Understand the problem statement
The problem asks for counting all possible paths from the top-left corner to the bottom-right corner of a grid, moving only right or down.Step 2: Identify the meaning of "Unique Paths"
Unique paths means counting distinct ways, not shortest or maximum sum paths.Final Answer:
The number of ways to move from top-left to bottom-right in a grid using only right and down moves -> Option BQuick Check:
Unique Paths = Count of right/down moves paths [OK]
- Confusing unique paths with shortest path length
- Thinking about obstacles when none are given
- Mixing path count with path sum
dp of size m x n with zeros for Unique Paths DP?Solution
Step 1: Understand Go slice initialization for 2D arrays
In Go, a 2D slice is created by first making the outer slice, then initializing each inner slice.Step 2: Check each option
dp := make([][]int, m) for i := range dp { dp[i] = make([]int, n) } correctly creates a slice of length m, then each inner slice of length n, all initialized to zero by default.Final Answer:
dp := make([][]int, m) for i := range dp { dp[i] = make([]int, n) } -> Option DQuick Check:
2D slice init with nested make loops = dp := make([][]int, m) for i := range dp { dp[i] = make([]int, n) } [OK]
- Using one-dimensional slice for 2D data
- Initializing inner slices with wrong length
- Appending slices incorrectly
func uniquePaths(m, n int) int {
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, n)
}
for i := 0; i < m; i++ {
dp[i][0] = 1
}
for j := 0; j < n; j++ {
dp[0][j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
dp[i][j] = dp[i-1][j] + dp[i][j-1]
}
}
return dp[m-1][n-1]
}
fmt.Println(uniquePaths(3, 3))Solution
Step 1: Initialize dp with 1s on first row and column
First row and first column are set to 1 because only one way to reach those cells (all right or all down).Step 2: Fill dp table using dp[i][j] = dp[i-1][j] + dp[i][j-1]
Calculate paths for each cell by summing paths from top and left cells. For 3x3 grid, dp[2][2] = 6.Final Answer:
6 -> Option CQuick Check:
Unique paths in 3x3 grid = 6 [OK]
- Not initializing first row and column to 1
- Using wrong indices in dp update
- Returning wrong dp cell
func uniquePaths(m, n int) int {
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, n)
}
for i := 0; i < m; i++ {
dp[i][0] = 1
}
for j := 0; j < n; j++ {
dp[0][j] = 1
}
for i := 1; i < m; i++ {
for j := 1; j < n; j++ {
dp[i][j] = dp[i-1][j] * dp[i][j-1]
}
}
return dp[m-1][n-1]
}Solution
Step 1: Check dp update formula
The code uses dp[i][j] = dp[i-1][j] * dp[i][j-1], which multiplies paths instead of adding them.Step 2: Correct formula for Unique Paths
The correct formula is dp[i][j] = dp[i-1][j] + dp[i][j-1] to sum the number of ways from top and left cells.Final Answer:
The dp update uses multiplication instead of addition -> Option AQuick Check:
Use addition, not multiplication in dp update [OK]
- Using * instead of + in dp update
- Forgetting to initialize first row/column
- Returning wrong dp cell
Obstacles grid:
0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 0
Solution
Step 1: Identify obstacle handling in DP
When a cell has an obstacle, no path can go through it, so dp[i][j] must be 0.Step 2: Modify dp update to skip obstacles
For cells without obstacles, dp[i][j] = dp[i-1][j] + dp[i][j-1]. For obstacles, dp[i][j] = 0.Final Answer:
Set dp[i][j] = 0 if obstacle at (i,j), else dp[i][j] = dp[i-1][j] + dp[i][j-1] -> Option AQuick Check:
Obstacles block paths, set dp to zero there [OK]
- Ignoring obstacles in dp calculation
- Setting dp to 1 at obstacles
- Multiplying dp values instead of adding
Ready to prove it?
Pass the test and this pattern moves unknown
