岛屿数量问题是LeetCode上经典的图论题目(编号200),也是面试中频繁出现的算法考题。题目要求计算二维网格中岛屿的数量,其中'1'代表陆地,'0'代表水域。岛屿由水平或垂直相邻的陆地连接形成,且假设网格四周都被水域包围。
这个问题本质上是对连通分量(Connected Components)的计数,属于图的遍历算法应用。在实际工程中,类似的算法可用于图像处理中的连通区域分析、社交网络中的社群发现等场景。
DFS是解决岛屿问题的直观选择。当遇到陆地时,通过递归或栈的方式探索所有相连的陆地,并标记为已访问。以下是Python实现的核心代码:
python复制def numIslands(grid):
if not grid:
return 0
count = 0
rows, cols = len(grid), len(grid[0])
for i in range(rows):
for j in range(cols):
if grid[i][j] == '1':
dfs(grid, i, j)
count += 1
return count
def dfs(grid, i, j):
if i<0 or j<0 or i>=len(grid) or j>=len(grid[0]) or grid[i][j]!='1':
return
grid[i][j] = '0' # 标记为已访问
dfs(grid, i+1, j)
dfs(grid, i-1, j)
dfs(grid, i, j+1)
dfs(grid, i, j-1)
时间复杂度:O(M×N),其中M和N分别是网格的行数和列数。每个网格点最多被访问一次。
空间复杂度:O(M×N),最坏情况下整个网格都是陆地,递归深度达到M×N。
BFS使用队列来实现,同样有效但避免了递归可能导致的栈溢出风险:
python复制from collections import deque
def numIslands(grid):
if not grid:
return 0
count = 0
rows, cols = len(grid), len(grid[0])
for i in range(rows):
for j in range(cols):
if grid[i][j] == '1':
bfs(grid, i, j)
count += 1
return count
def bfs(grid, i, j):
queue = deque()
queue.append((i,j))
grid[i][j] = '0'
while queue:
x, y = queue.popleft()
for dx, dy in [(1,0), (-1,0), (0,1), (0,-1)]:
nx, ny = x+dx, y+dy
if 0<=nx<len(grid) and 0<=ny<len(grid[0]) and grid[nx][ny]=='1':
grid[nx][ny] = '0'
queue.append((nx, ny))
时间复杂度与DFS相同,空间复杂度在最坏情况下也是O(M×N)。
对于大规模网格,并查集提供了另一种解决方案:
python复制class UnionFind:
def __init__(self, grid):
m, n = len(grid), len(grid[0])
self.count = 0
self.parent = [0] * (m*n)
self.rank = [0] * (m*n)
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
self.parent[i*n + j] = i*n + j
self.count += 1
def find(self, i):
if self.parent[i] != i:
self.parent[i] = self.find(self.parent[i])
return self.parent[i]
def union(self, x, y):
rootx = self.find(x)
rooty = self.find(y)
if rootx != rooty:
if self.rank[rootx] > self.rank[rooty]:
self.parent[rooty] = rootx
elif self.rank[rootx] < self.rank[rooty]:
self.parent[rootx] = rooty
else:
self.parent[rooty] = rootx
self.rank[rootx] += 1
self.count -= 1
def numIslands(grid):
if not grid:
return 0
uf = UnionFind(grid)
m, n = len(grid), len(grid[0])
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
grid[i][j] = '0'
for di, dj in [(1,0), (-1,0), (0,1), (0,-1)]:
ni, nj = i+di, j+dj
if 0<=ni<m and 0<=nj<n and grid[ni][nj]=='1':
uf.union(i*n+j, ni*n+nj)
return uf.count
时间复杂度:O(M×N×α(M×N)),其中α是反阿克曼函数,增长极其缓慢。
空间复杂度:O(M×N),用于存储父节点和秩数组。
原始方法修改了输入网格,如果不允许修改输入,可以使用额外的访问矩阵:
python复制visited = [[False for _ in range(cols)] for _ in range(rows)]
这会增加O(M×N)的空间复杂度。更节省空间的做法是用位运算压缩访问状态。
对于超大规模网格(如卫星图像处理),可以考虑:
python复制directions = [(-1,0), (1,0), (0,-1), (0,1)]
有效测试用例应包括:
python复制[
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
在真实工程中,还需要考虑: