title: Number of Islands slug: number-of-islands difficulty: medium leetcode_id: 200 leetcode_url: https://leetcode.com/problems/number-of-islands/ categories: - graphs - arrays patterns: - dfs - bfs 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. 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' examples: - input: | grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] output: "1" explanation: "All land cells are connected, forming one island." - input: | grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] output: "3" explanation: "Three separate connected components of land." 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. 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. common_pitfalls: - title: Not marking visited cells description: | Without marking cells as visited, you'll count the same island multiple times or get infinite loops in DFS/BFS. wrong_approach: "Not modifying grid or using visited set" correct_approach: "Mark cell as '0' or add to visited set when processing" - title: Diagonal connections description: | Islands only connect horizontally and vertically, not diagonally. Only explore 4 directions, not 8. - title: Boundary checks description: | Always check if row/col are within bounds before accessing grid. 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 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. solutions: - approach_name: DFS (Optimal) is_optimal: true code: | def num_islands(grid: list[list[str]]) -> int: if not grid: return 0 rows, cols = len(grid), len(grid[0]) islands = 0 def dfs(r: int, c: int) -> None: 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 dfs(r + 1, c) dfs(r - 1, c) dfs(r, c + 1) dfs(r, c - 1) for r in range(rows): for c in range(cols): if grid[r][c] == '1': 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. - approach_name: BFS is_optimal: true code: | from collections import deque def num_islands(grid: list[list[str]]) -> int: if not grid: return 0 rows, cols = len(grid), len(grid[0]) islands = 0 def bfs(start_r: int, start_c: int) -> None: queue = deque([(start_r, start_c)]) grid[start_r][start_c] = '0' while queue: r, c = queue.popleft() for dr, dc in [(1, 0), (-1, 0), (0, 1), (0, -1)]: nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == '1': grid[nr][nc] = '0' queue.append((nr, nc)) for r in range(rows): for c in range(cols): if grid[r][c] == '1': islands += 1 bfs(r, c) return islands explanation: | Same logic using BFS instead of DFS. Avoids recursion stack but uses queue space.