63. Unique Paths II
At a Glance
- Topic: dp-2d
- Pattern: Dynamic Programming (grid with obstacles)
- Difficulty: Medium
- Companies: Amazon, Google, Bloomberg, Microsoft, Oracle
- Frequency: High
- LeetCode: 63
Problem (one-liner)
Same movement as Unique Paths, but grid[row][col] == 1 marks an obstacle (cannot enter). Count paths from top-left to bottom-right. Input: obstacleGrid. Output: path count.
Recognition Cues
- “Obstacle” / value
1blocks cell - First row/column can be blocked → cascade zeros
- Same recurrence when cell not blocked
Diagram
At-a-glance flow (replace with problem-specific Mermaid as you refine this note). camelCase node IDs; no spaces in IDs.
Loading diagram…
Approaches
- Brute force — DFS — exponential.
- Better — full 2D DP —
O(m*n)time /O(m*n)space. - Optimal — one-row rolling with obstacle checks —
O(m*n)time /O(n)space. <- pick this in interview.
Optimal Solution
Go
package main
func uniquePathsWithObstaclesTable(obstacleGrid [][]int) int {
rows := len(obstacleGrid)
if rows == 0 {
return 0
}
cols := len(obstacleGrid[0])
table := make([][]int, rows)
for row := range table {
table[row] = make([]int, cols)
}
if obstacleGrid[0][0] == 1 {
return 0
}
table[0][0] = 1
for col := 1; col < cols; col++ {
if obstacleGrid[0][col] == 1 {
table[0][col] = 0
} else {
table[0][col] = table[0][col-1]
}
}
for row := 1; row < rows; row++ {
if obstacleGrid[row][0] == 1 {
table[row][0] = 0
} else {
table[row][0] = table[row-1][0]
}
for col := 1; col < cols; col++ {
if obstacleGrid[row][col] == 1 {
table[row][col] = 0
} else {
table[row][col] = table[row-1][col] + table[row][col-1]
}
}
}
return table[rows-1][cols-1]
}
func uniquePathsWithObstacles(obstacleGrid [][]int) int {
rows := len(obstacleGrid)
if rows == 0 {
return 0
}
cols := len(obstacleGrid[0])
row := make([]int, cols)
if obstacleGrid[0][0] == 1 {
return 0
}
row[0] = 1
for col := 1; col < cols; col++ {
if obstacleGrid[0][col] == 1 {
row[col] = 0
} else {
row[col] = row[col-1]
}
}
for iterRow := 1; iterRow < rows; iterRow++ {
if obstacleGrid[iterRow][0] == 1 {
row[0] = 0
}
for col := 1; col < cols; col++ {
if obstacleGrid[iterRow][col] == 1 {
row[col] = 0
} else {
row[col] += row[col-1]
}
}
}
return row[cols-1]
}JavaScript
function uniquePathsWithObstacles(obstacleGrid) {
const rows = obstacleGrid.length;
if (rows === 0) return 0;
const cols = obstacleGrid[0].length;
if (obstacleGrid[0][0] === 1) return 0;
const row = new Array(cols).fill(0);
row[0] = 1;
for (let col = 1; col < cols; col++) {
row[col] = obstacleGrid[0][col] === 1 ? 0 : row[col - 1];
}
for (let iterRow = 1; iterRow < rows; iterRow++) {
if (obstacleGrid[iterRow][0] === 1) {
row[0] = 0;
}
for (let col = 1; col < cols; col++) {
if (obstacleGrid[iterRow][col] === 1) {
row[col] = 0;
} else {
row[col] += row[col - 1];
}
}
}
return row[cols - 1];
}Walkthrough
grid = [[0,0,0],[0,1,0],[0,0,0]]
First row: [1,1,1] until blocked row...
Row with obstacle at center zeros that cell; paths recombine below.
Edge Cases
- Start or goal blocked →
0 - Entire first row blocked before last column →
0
Pitfalls
- Mutating input when reuse forbidden
- Initializing first row without obstacle propagation
Similar Problems
- 62. Unique Paths — no obstacles baseline.
- 322. Coin Change — different domain, same “ways to reach sum” intuition.
- 518. Coin Change 2 — counting compositions (unbounded).
Variants
- Minimum cost with obstacles → add weights.
Mind-Map Tags
#grid-dp #obstacles #rolling-row #medium
Last updated on
Spotted something unclear or wrong on this page?