feat(content): test cases batch 2

This commit is contained in:
2025-05-24 21:00:16 +01:00
parent 2e323d7a06
commit 09ec96a282
10 changed files with 962 additions and 474 deletions

View File

@@ -9,19 +9,18 @@ categories:
patterns:
- dfs
- bfs
- matrix-traversal
description: |
Given an m x n 2D binary grid `grid` which represents a map of '1's (land) and '0's (water),
return the number of islands.
Given an `m × n` 2D binary grid `grid` which represents a map of `'1'`s (land) and `'0'`s (water), return *the number of islands*.
An island is surrounded by water and is formed by connecting adjacent lands horizontally
or vertically. You may assume all four edges of the grid are surrounded by water.
An **island** is surrounded by water and is formed by connecting adjacent lands **horizontally or vertically**. You may assume all four edges of the grid are surrounded by water.
constraints: |
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 300
- grid[i][j] is '0' or '1'
- `m == grid.length`
- `n == grid[i].length`
- `1 <= m, n <= 300`
- `grid[i][j]` is `'0'` or `'1'`
examples:
- input: |
@@ -32,7 +31,7 @@ examples:
["0","0","0","0","0"]
]
output: "1"
explanation: "All land cells are connected, forming one island."
explanation: "All land cells are connected horizontally/vertically, forming one island."
- input: |
grid = [
["1","1","0","0","0"],
@@ -41,53 +40,93 @@ examples:
["0","0","0","1","1"]
]
output: "3"
explanation: "Three separate connected components of land."
explanation: "Three separate groups of connected land cells — three islands."
explanation:
approach: |
1. Iterate through every cell in the grid
2. When a '1' (land) is found, increment island count
3. Use DFS/BFS to mark all connected land cells as visited
4. Continue iteration until all cells are processed
intuition: |
Each island is a connected component of '1's. We need to count these components.
Imagine looking at a map from above. Each `'1'` is a piece of land, and you want to count how many distinct landmasses (islands) exist. Two pieces of land belong to the same island if you can walk from one to the other without crossing water (moving only up, down, left, or right — not diagonally).
When we find an unvisited '1', we've discovered a new island. We then "sink" the entire
island by marking all connected '1's as visited (either change to '0' or use a visited set).
This ensures we don't count the same island multiple times.
Think of it like this: when you step onto a piece of land, you want to "explore" the entire island by visiting all connected land cells. Once you've seen the whole island, you mark it as "visited" so you don't count it again. Then you continue scanning the map for the next unvisited piece of land.
This is the classic **connected components** problem on a grid. Each island is one connected component of `'1'`s. We count components by:
1. Finding an unvisited land cell (new island found!)
2. Exploring all connected land cells (mark the whole island as visited)
3. Repeat until every cell has been processed
approach: |
We solve this using **DFS to Explore and Mark Islands**:
**Step 1: Iterate through every cell**
- Scan the grid row by row, column by column
- We're looking for unvisited `'1'`s — each one represents a new island
&nbsp;
**Step 2: When land is found, count it and explore**
- Increment the island count
- Use DFS (or BFS) to visit all connected land cells
- Mark each visited cell by changing `'1'` to `'0'` (this "sinks" the island to avoid recounting)
&nbsp;
**Step 3: DFS exploration**
- From the current cell, recursively explore all four directions (up, down, left, right)
- Stop when: out of bounds, or cell is water (`'0'`)
- Mark the cell as visited **before** recursive calls to prevent infinite loops
&nbsp;
**Step 4: Return the count**
- After processing all cells, return the island count
&nbsp;
This works because once we've explored an island, all its cells are marked as `'0'`, so we'll never trigger a new exploration from those cells again.
common_pitfalls:
- title: Not marking visited cells
- title: Not Marking Cells as Visited
description: |
Without marking cells as visited, you'll count the same island multiple times
or get infinite loops in DFS/BFS.
Without marking visited cells, two things go wrong:
1. You'll count the same island multiple times (each cell triggers a new count)
2. DFS/BFS will revisit cells infinitely, causing a stack overflow or infinite loop
The cleanest solution is to modify the grid itself — change `'1'` to `'0'` when visited. Alternatively, use a separate `visited` set, but this uses extra space.
wrong_approach: "Not modifying grid or using visited set"
correct_approach: "Mark cell as '0' or add to visited set when processing"
correct_approach: "grid[r][c] = '0' immediately when visiting"
- title: Diagonal connections
- title: Including Diagonal Connections
description: |
Islands only connect horizontally and vertically, not diagonally.
Only explore 4 directions, not 8.
The problem states islands connect **horizontally or vertically** only. Diagonal cells are NOT considered adjacent.
- title: Boundary checks
Check only 4 directions: `(r+1,c), (r-1,c), (r,c+1), (r,c-1)`. Don't include the 4 diagonal directions.
wrong_approach: "Exploring 8 directions including diagonals"
correct_approach: "Explore only 4 orthogonal directions"
- title: Boundary Check Errors
description: |
Always check if row/col are within bounds before accessing grid.
Before accessing `grid[r][c]`, always verify that `r` and `c` are within bounds:
- `0 <= r < rows`
- `0 <= c < cols`
Missing these checks causes index-out-of-bounds errors.
wrong_approach: "Accessing grid[r][c] without bounds check"
correct_approach: "Check bounds first: if r < 0 or r >= rows or c < 0 or c >= cols: return"
key_takeaways:
- Grid problems often reduce to graph traversal
- DFS or BFS both work for exploring connected components
- Modifying input can serve as "visited" tracking
- This pattern applies to many "count components" problems
- "**Grid = implicit graph**: Each cell is a node; adjacent cells are connected by edges"
- "**DFS/BFS for connected components**: Classic technique for counting or exploring connected regions"
- "**In-place marking**: Modifying input to track visited state saves space (when allowed)"
- "**Foundation for many grid problems**: Flood fill, maze solving, region counting all use this pattern"
time_complexity: "O(m × n)"
space_complexity: "O(m × n)"
complexity_explanation: |
Time: Each cell is visited at most once.
Space: DFS recursion stack or BFS queue can hold O(m × n) cells in worst case.
time_complexity: "O(m × n). Each cell is visited at most once by the main loop and at most once by DFS/BFS."
space_complexity: "O(m × n). In the worst case (all land), the DFS recursion stack or BFS queue can hold all cells."
solutions:
- approach_name: DFS (Optimal)
- approach_name: DFS
is_optimal: true
code: |
def num_islands(grid: list[list[str]]) -> int:
@@ -98,28 +137,36 @@ solutions:
islands = 0
def dfs(r: int, c: int) -> None:
# Stop if out of bounds or water
if r < 0 or r >= rows or c < 0 or c >= cols:
return
if grid[r][c] != '1':
return
grid[r][c] = '0' # Mark as visited
# Mark as visited by "sinking" the land
grid[r][c] = '0'
dfs(r + 1, c)
dfs(r - 1, c)
dfs(r, c + 1)
dfs(r, c - 1)
# Explore all four directions
dfs(r + 1, c) # down
dfs(r - 1, c) # up
dfs(r, c + 1) # right
dfs(r, c - 1) # left
# Scan every cell in the grid
for r in range(rows):
for c in range(cols):
if grid[r][c] == '1':
# Found new island! Count it and explore
islands += 1
dfs(r, c)
return islands
explanation: |
When land is found, increment count and sink the entire island using DFS.
Modifying the grid serves as our visited marker.
**Time Complexity:** O(m × n) — Each cell visited at most twice (once by loop, once by DFS).
**Space Complexity:** O(m × n) — Recursion stack in worst case (grid is all land in a snake pattern).
When we find unvisited land, we increment our count and use DFS to "sink" the entire island by marking all connected land as water. This prevents recounting.
- approach_name: BFS
is_optimal: true
@@ -135,14 +182,18 @@ solutions:
def bfs(start_r: int, start_c: int) -> None:
queue = deque([(start_r, start_c)])
grid[start_r][start_c] = '0'
grid[start_r][start_c] = '0' # Mark starting cell
while queue:
r, c = queue.popleft()
# Explore all four directions
for dr, dc in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
nr, nc = r + dr, c + dc
# Add unvisited land to queue
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == '1':
grid[nr][nc] = '0'
grid[nr][nc] = '0' # Mark before adding to queue
queue.append((nr, nc))
for r in range(rows):
@@ -153,5 +204,8 @@ solutions:
return islands
explanation: |
Same logic using BFS instead of DFS.
Avoids recursion stack but uses queue space.
**Time Complexity:** O(m × n) — Same as DFS.
**Space Complexity:** O(min(m, n)) — Queue holds at most one "frontier" layer, which is bounded by the smaller dimension.
BFS explores level by level rather than depth-first. Mark cells as visited **when adding to queue** (not when processing) to avoid adding duplicates.