在问 · 算法题解

200. 岛屿数量

中等图论广度优先搜索力扣原题 ↗

题目描述

输入一个 m x n 的二维网格 grid,每个格子是字符 '1'(陆地)或 '0'(水)。

上下左右相邻的陆地格子属于同一座岛,斜向相邻不算相连。每座岛四周都是水,网格外部也都视为水。

返回网格中岛屿的个数。

示例 1
输入:grid = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]
输出:1

解释:所有陆地格子彼此连通,只有 1 座岛。

示例 2
输入:grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]]
输出:3

解释:左上角 2×2 的陆地是一座,中间单独的 '1' 是一座,右下角两个 '1' 是一座,共 3 座。

数据范围

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] 为 '0' 或 '1'。

解题思路:广度优先搜索

用队列或集合维护图上的可达状态。

为什么正确

状态队列中的元素不会重复处理。

复杂度分析

时间 O(mn),额外空间 O(min(m,n))。

易错点

注意边界条件并在回溯或弹出后恢复状态。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

from collections import deque

class Solution:
    def numIslands(self, grid):
        if not grid:
            return 0
        m, n = len(grid), len(grid[0])
        count = 0
        for r in range(m):
            for c in range(n):
                if grid[r][c] == '1':
                    count += 1
                    grid[r][c] = '0'
                    queue = deque([(r, c)])
                    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 < m and 0 <= ny < n and grid[nx][ny] == '1':
                                grid[nx][ny] = '0'
                                queue.append((nx, ny))
        return count

载入编辑器,自己跑一遍 →

其他解法

BFS 和 DFS 都是从一块陆地出发“淘污”整座岛,只是扩展顺序不同,DFS 可能递归很深;并查集把问题看成统计连通分量的个数,通过合并相邻陆地得到答案。

解法 2:深度优先搜索 时间 O(mn),空间 O(mn)

扫描网格,遇到 '1' 就把岛屿数加一,并从这里递归地向上下左右扩展,把连通的 '1' 全部改成 '0'。递归遇到越界或 '0' 时返回。这样每座岛屿只会被计数一次。

代码最短,不需要队列。最坏情况下整张网格都是陆地,递归深度可达 mn,有栈溢出的风险。

import sys
from typing import List

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        sys.setrecursionlimit(max(10000, len(grid) * len(grid[0]) + 100))
        m, n = len(grid), len(grid[0])

        def dfs(i, j):
            if i < 0 or i >= m or j < 0 or j >= n or grid[i][j] != '1':
                return
            grid[i][j] = '0'
            dfs(i + 1, j)
            dfs(i - 1, j)
            dfs(i, j + 1)
            dfs(i, j - 1)

        count = 0
        for i in range(m):
            for j in range(n):
                if grid[i][j] == '1':
                    count += 1
                    dfs(i, j)
        return count

解法 3:并查集 时间 O(mn·α(mn)),空间 O(mn)

把每个格子编号为 i*n+j,初始时每个陆地各自成一个集合,岛屿数 count 等于陆地格子数。再遍历每个陆地,如果右边或下边也是陆地,就把两个格子合并;每次成功合并两个不同集合,count 减一。最后的 count 就是岛屿数。

不修改原网格,也不需要搜索,并且可以自然推广到陆地动态增加的场景(如第 305 题)。需要实现并查集,代码更长。

from typing import List

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        m, n = len(grid), len(grid[0])
        parent = list(range(m * n))

        def find(x):
            while parent[x] != x:
                parent[x] = parent[parent[x]]
                x = parent[x]
            return x

        count = sum(row.count('1') for row in grid)
        for i in range(m):
            for j in range(n):
                if grid[i][j] != '1':
                    continue
                for x, y in ((i + 1, j), (i, j + 1)):
                    if x < m and y < n and grid[x][y] == '1':
                        ra, rb = find(i * n + j), find(x * n + y)
                        if ra != rb:
                            parent[ra] = rb
                            count -= 1
        return count

图论 专题的其他题目

查看 图论 全部题目 →