Given a 2D grid grid where '1' represents land and '0' represents water, count and return the number of islands.
An island is formed by connecting adjacent lands horizontally or vertically and is surrounded by water. You may assume water is surrounding the grid (i.e., all the edges are water).
Example 1:
Input: grid = [
["0","1","1","1","0"],
["0","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1Example 2:
Input: grid = [
["1","1","0","0","1"],
["1","1","0","0","1"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 4Constraints:
1 <= grid.length, grid[i].length <= 100grid[i][j] is '0' or '1'.
You should aim for a solution with O(m * n) time and O(m * n) space, where m is the number of rows and n is the number of columns in the grid.
An island is a group of 1's in which every 1 is reachable from any other 1 in that group. Can you think of an algorithm that can find the number of groups by visiting a group only once? Maybe there is a recursive way of doing it.
We can use the Depth First Search (DFS) algorithm to traverse each group independently. We iterate through each cell of the grid. When we encounter a 1, we perform a DFS starting at that cell and recursively visit every other 1 that is reachable. During this process, we mark the visited 1's as 0 to ensure we don't revisit them, as they belong to the same group. The number of groups corresponds to the number of islands.
Before attempting this problem, you should be comfortable with:
Think of the grid as a map where '1' is land and '0' is water.
An island is a group of connected land cells (up, down, left, right).
Whenever we find a land cell that hasn’t been visited, we start a DFS to sink the entire island by marking all its connected land as water. Each DFS call corresponds to one island.
'1' is found:'0', return.'0' (visited).class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
ROWS, COLS = len(grid), len(grid[0])
islands = 0
def dfs(r, c):
if (r < 0 or c < 0 or r >= ROWS or
c >= COLS or grid[r][c] == "0"
):
return
grid[r][c] = "0"
for dr, dc in directions:
dfs(r + dr, c + dc)
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == "1":
dfs(r, c)
islands += 1
return islandsWhere is the number of rows and is the number of columns in the .
Treat the grid like a map where '1' represents land and '0' represents water.
Each island is a group of connected land cells.
When we encounter a land cell, we use BFS to visit all connected land cells and mark them as water, ensuring the same island is not counted again.
'1' (land) cell is found:'0'.'0' and add it to the queue.class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
ROWS, COLS = len(grid), len(grid[0])
islands = 0
def bfs(r, c):
q = deque()
grid[r][c] = "0"
q.append((r, c))
while q:
row, col = q.popleft()
for dr, dc in directions:
nr, nc = dr + row, dc + col
if (nr < 0 or nc < 0 or nr >= ROWS or
nc >= COLS or grid[nr][nc] == "0"
):
continue
q.append((nr, nc))
grid[nr][nc] = "0"
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == "1":
bfs(r, c)
islands += 1
return islandsWhere is the number of rows and is the number of columns in the .
Think of every land cell ('1') as its own separate island initially.
When two land cells are adjacent (up, down, left, right), they actually belong to the same island, so we should merge them.
Disjoint Set Union (Union-Find) helps us:
Each successful merge reduces the total island count by 1.
(row, col) to a unique index.'1'), increment island count.class DSU:
def __init__(self, n):
self.Parent = list(range(n + 1))
self.Size = [1] * (n + 1)
def find(self, node):
if self.Parent[node] != node:
self.Parent[node] = self.find(self.Parent[node])
return self.Parent[node]
def union(self, u, v):
pu = self.find(u)
pv = self.find(v)
if pu == pv:
return False
if self.Size[pu] >= self.Size[pv]:
self.Size[pu] += self.Size[pv]
self.Parent[pv] = pu
else:
self.Size[pv] += self.Size[pu]
self.Parent[pu] = pv
return True
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
dsu = DSU(ROWS * COLS)
def index(r, c):
return r * COLS + c
directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
islands = 0
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == '1':
islands += 1
for dr, dc in directions:
nr, nc = r + dr, c + dc
if (nr < 0 or nc < 0 or nr >= ROWS or
nc >= COLS or grid[nr][nc] == "0"
):
continue
if dsu.union(index(r, c), index(nr, nc)):
islands -= 1
return islandsWhere is the number of rows and is the number of columns in the .
When exploring an island, you must mark cells as visited (either by changing them to '0' or using a visited set). Forgetting this causes infinite loops as the DFS/BFS keeps revisiting the same cells.
# Wrong: doesn't mark visited, infinite loop
if grid[r][c] == '1':
dfs(r + 1, c)
# Correct: mark as visited
if grid[r][c] == '1':
grid[r][c] = '0' # or visited.add((r, c))
dfs(r + 1, c)Each island should be counted once when you first encounter it. A common mistake is incrementing the count for every '1' cell rather than only incrementing when starting a new DFS/BFS traversal.
Always verify that row and column indices are within bounds before accessing the grid. Off-by-one errors or missing boundary checks cause index out of bounds errors.
# Wrong: checks after access
if grid[nr][nc] == '1' and 0 <= nr < rows:
# Correct: check bounds first
if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == '1':This problem only considers horizontal and vertical connections (4-directional). Including diagonal neighbors incorrectly merges separate islands and undercounts the total.
Sign in to join the discussion