questions S-W

This commit is contained in:
2025-05-30 19:18:33 +01:00
parent 0a0feb93b5
commit ef4c4dcf17
46 changed files with 9696 additions and 0 deletions

View File

@@ -0,0 +1,289 @@
title: Surrounded Regions
slug: surrounded-regions
difficulty: medium
leetcode_id: 130
leetcode_url: https://leetcode.com/problems/surrounded-regions/
categories:
- graphs
- arrays
patterns:
- dfs
- bfs
- matrix-traversal
description: |
You are given an `m x n` matrix `board` containing letters `'X'` and `'O'`. **Capture** all regions that are **surrounded**:
- **Connect**: A cell is connected to adjacent cells horizontally or vertically.
- **Region**: To form a region, connect every `'O'` cell.
- **Surround**: A region is surrounded with `'X'` cells if you can connect the region with `'X'` cells and **none** of the region cells are on the edge of the board.
To capture a surrounded region, replace all `'O'`s with `'X'`s **in-place** within the original board. You do not need to return anything.
constraints: |
- `m == board.length`
- `n == board[i].length`
- `1 <= m, n <= 200`
- `board[i][j]` is `'X'` or `'O'`
examples:
- input: 'board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]]'
output: '[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]]'
explanation: "The O's in the center form a surrounded region and are captured. The O at the bottom-left edge is not surrounded (it touches the boundary), so it remains unchanged."
- input: 'board = [["X"]]'
output: '[["X"]]'
explanation: "A single X cell has no O's to capture."
explanation:
intuition: |
Imagine the board as an island map where `'O'` cells are land and `'X'` cells are water. A region of `'O'`s is "surrounded" if it has no connection to the boundary of the map — it's completely enclosed by water.
The **key insight** is to think about this problem *backwards*: instead of finding regions that ARE surrounded, find regions that are NOT surrounded (those connected to the boundary), and protect them. Everything else gets captured.
Think of it like this: any `'O'` on the edge of the board is automatically safe — it can never be fully surrounded. Furthermore, any `'O'` connected to an edge `'O'` is also safe, because the whole connected component touches the boundary.
So the strategy becomes:
1. Start from all `'O'`s on the boundary
2. Mark all `'O'`s connected to them as "safe"
3. Everything NOT marked safe is surrounded and should be captured
This "reverse thinking" transforms a complex region-finding problem into a simpler boundary-flood problem.
approach: |
We solve this using a **Boundary DFS/BFS Approach**:
**Step 1: Identify boundary O's**
- Iterate through all cells on the four edges of the board (first row, last row, first column, last column)
- When you find an `'O'` on the boundary, it cannot be captured
&nbsp;
**Step 2: Mark safe regions**
- From each boundary `'O'`, perform DFS (or BFS) to visit all connected `'O'` cells
- Mark these cells with a temporary marker (e.g., `'T'` for "temporary" or "safe")
- This marks the entire connected component as uncapturable
&nbsp;
**Step 3: Capture and restore**
- Iterate through the entire board
- Any `'O'` remaining is surrounded — convert it to `'X'`
- Any `'T'` (temporary marker) is safe — restore it to `'O'`
&nbsp;
This three-phase approach ensures we correctly identify which regions touch the boundary and which are truly surrounded.
common_pitfalls:
- title: Trying to Find Surrounded Regions Directly
description: |
A natural first instinct is to iterate through the board, find each `'O'` region, and check if it's surrounded. This is complex because you need to:
- Track all cells in a region
- Check if ANY cell touches the boundary
- Only then decide to capture or not
The boundary-first approach is simpler: mark safe cells first, then capture everything else in one pass.
wrong_approach: "Find each O region and check if surrounded"
correct_approach: "Mark boundary-connected O's first, then capture the rest"
- title: Forgetting to Check All Four Boundaries
description: |
The board has four edges: top row, bottom row, left column, and right column. Missing any edge means some safe `'O'`s won't be marked, leading to incorrect captures.
Make sure to iterate through:
- Row 0 and row `m-1` (top and bottom)
- Column 0 and column `n-1` (left and right)
wrong_approach: "Only checking top and left edges"
correct_approach: "Check all four edges of the board"
- title: Stack Overflow with Deep Recursion
description: |
With a 200x200 board, a region could contain up to 40,000 cells. Naive recursive DFS might cause stack overflow on such large connected components.
Solutions:
- Use iterative DFS with an explicit stack
- Use BFS with a queue
- Increase recursion limit (not recommended)
wrong_approach: "Deep recursive DFS on large boards"
correct_approach: "Iterative DFS or BFS for large inputs"
- title: Modifying While Searching
description: |
If you try to capture `'O'`s to `'X'`s while still searching, you might accidentally disconnect parts of a safe region before fully exploring it.
The temporary marker (`'T'`) prevents this — it distinguishes "visited safe" cells from both `'O'` (unvisited) and `'X'` (wall/captured).
key_takeaways:
- "**Reverse thinking**: Sometimes it's easier to find what you DON'T want and protect it, rather than directly finding what you want"
- "**Boundary-connected components**: Problems involving 'surrounded' often reduce to finding what's connected to the boundary"
- "**Temporary markers**: Using a third state (`'T'`) allows clean separation of visited-safe, unvisited, and captured cells"
- "**Pattern recognition**: This is similar to Number of Islands, but with the twist of boundary connectivity — recognise the DFS/BFS on grids pattern"
time_complexity: "O(m * n). We visit each cell at most twice: once during the boundary DFS/BFS marking phase, and once during the final capture/restore pass."
space_complexity: "O(m * n) in the worst case for the recursion stack or BFS queue, if almost all cells are `'O'` and connected. The modification is done in-place, so no additional board copy is needed."
solutions:
- approach_name: Boundary DFS
is_optimal: true
code: |
def solve(board: list[list[str]]) -> None:
if not board or not board[0]:
return
m, n = len(board), len(board[0])
def dfs(r: int, c: int) -> None:
# Out of bounds or not an O — stop
if r < 0 or r >= m or c < 0 or c >= n or board[r][c] != 'O':
return
# Mark as safe (temporary marker)
board[r][c] = 'T'
# Explore all four directions
dfs(r + 1, c) # down
dfs(r - 1, c) # up
dfs(r, c + 1) # right
dfs(r, c - 1) # left
# Step 1 & 2: Mark all O's connected to boundary
for i in range(m):
dfs(i, 0) # left edge
dfs(i, n - 1) # right edge
for j in range(n):
dfs(0, j) # top edge
dfs(m - 1, j) # bottom edge
# Step 3: Capture surrounded O's, restore safe T's
for i in range(m):
for j in range(n):
if board[i][j] == 'O':
board[i][j] = 'X' # Capture surrounded
elif board[i][j] == 'T':
board[i][j] = 'O' # Restore safe
explanation: |
**Time Complexity:** O(m * n) — Each cell is visited at most twice.
**Space Complexity:** O(m * n) — Recursion stack in worst case.
We start DFS from every boundary `'O'`, marking connected cells as `'T'` (safe). Then we sweep through the board: remaining `'O'`s are captured, `'T'`s are restored. This cleanly separates boundary-connected regions from surrounded ones.
- approach_name: Boundary BFS
is_optimal: true
code: |
from collections import deque
def solve(board: list[list[str]]) -> None:
if not board or not board[0]:
return
m, n = len(board), len(board[0])
queue = deque()
# Step 1: Collect all boundary O's
for i in range(m):
if board[i][0] == 'O':
queue.append((i, 0))
if board[i][n - 1] == 'O':
queue.append((i, n - 1))
for j in range(n):
if board[0][j] == 'O':
queue.append((0, j))
if board[m - 1][j] == 'O':
queue.append((m - 1, j))
# Step 2: BFS to mark all safe O's
while queue:
r, c = queue.popleft()
if r < 0 or r >= m or c < 0 or c >= n:
continue
if board[r][c] != 'O':
continue
board[r][c] = 'T' # Mark as safe
# Add neighbors to explore
queue.append((r + 1, c))
queue.append((r - 1, c))
queue.append((r, c + 1))
queue.append((r, c - 1))
# Step 3: Capture and restore
for i in range(m):
for j in range(n):
if board[i][j] == 'O':
board[i][j] = 'X' # Capture
elif board[i][j] == 'T':
board[i][j] = 'O' # Restore
explanation: |
**Time Complexity:** O(m * n) — Each cell processed at most once.
**Space Complexity:** O(m * n) — Queue size in worst case.
BFS avoids recursion depth issues. We seed the queue with all boundary `'O'`s, then expand outward marking safe cells. The final pass captures and restores just like the DFS approach. BFS is often preferred for very large grids.
- approach_name: Union-Find
is_optimal: false
code: |
def solve(board: list[list[str]]) -> None:
if not board or not board[0]:
return
m, n = len(board), len(board[0])
# Union-Find with path compression
parent = list(range(m * n + 1))
rank = [0] * (m * n + 1)
dummy = m * n # Virtual node for boundary-connected cells
def find(x: int) -> int:
if parent[x] != x:
parent[x] = find(parent[x]) # Path compression
return parent[x]
def union(x: int, y: int) -> None:
px, py = find(x), find(y)
if px == py:
return
# Union by rank
if rank[px] < rank[py]:
px, py = py, px
parent[py] = px
if rank[px] == rank[py]:
rank[px] += 1
def index(r: int, c: int) -> int:
return r * n + c
# Build unions
for i in range(m):
for j in range(n):
if board[i][j] != 'O':
continue
idx = index(i, j)
# Connect boundary O's to dummy node
if i == 0 or i == m - 1 or j == 0 or j == n - 1:
union(idx, dummy)
# Connect to adjacent O's
if i > 0 and board[i - 1][j] == 'O':
union(idx, index(i - 1, j))
if j > 0 and board[i][j - 1] == 'O':
union(idx, index(i, j - 1))
# Capture cells not connected to dummy
for i in range(m):
for j in range(n):
if board[i][j] == 'O' and find(index(i, j)) != find(dummy):
board[i][j] = 'X'
explanation: |
**Time Complexity:** O(m * n * α(m * n)) — Nearly linear due to path compression.
**Space Complexity:** O(m * n) — Parent and rank arrays.
Union-Find groups all `'O'`s into connected components. Boundary `'O'`s are connected to a virtual "dummy" node. After processing, any `'O'` not in the dummy's component is surrounded and captured. This approach is more complex but demonstrates the Union-Find pattern for connectivity problems.