type
status
date
summary
tags
category
icon
password
featured
freq
difficulty
给定一个由
'1'(陆地)和 '0'(水)组成的的二维网格,计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。可以假设该网格的四条边均被水包围。

示例 1:
示例 2:
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j]的值为'0'或'1'
解法一:DFS
这题是经典的在网格里数连通块的问题,核心就是遇到一块陆地,就把整块岛都“淹掉”,计数+1。
可以把整个
grid 看成一张图:'1'是陆地节点
'0'是水,不连通
我们从左上到右下扫一遍网格:
- 一旦看到一个还没被处理过的
'1',就说明发现了一座新岛,答案先 +1。
- 然后从这个格子出发,往四个方向(上、下、左、右)递归 dfs 把能连到的陆地都“染掉”。
- 染色的方式很简单粗暴:直接把
'1'改成'0',表示“已经访问过 / 已经淹掉”。
这样,当我们继续往后遍历时,就不会重复数同一座岛了。
整个过程就是:发现岛屿入口 → 一路灌水把整块岛全变成 0。
用递归 DFS 写起来非常直观,但如果地图非常大、某一块岛非常连成一大片,递归深度可能比较深(Python 可能有栈深限制)。
复杂度
- 时间:O(m·n),每个格子最多被访问一次。
- 空间:最坏 O(m·n)(递归栈深度)。
解法二:BFS (推荐)
BFS 的思路和 DFS 一样,都是发现一块岛就整块灌水。
它们只是遍历一块岛的方法不同,区别在:
DFS = 一条路一直往下
BFS = 从起点一圈一圈向外扩散
具体做法:
- 扫描网格,遇到
'1'就说明发现新岛,结果 +1。
- 把这个格子加进队列,然后开始做 BFS:
- 从队列中弹出一个格子,把它视为当前“水淹到的地方”;
- 看它四个方向有没有
'1',有的话就入队,并立刻改成'0'标记成“访问过”。
- 队列空了,说明这块岛全部处理完了,然后继续扫网格,找下一块岛。
和 DFS 一样,本质是遍历整块连通区域。BFS用队列代替递归栈,更不容易出现栈溢出。
复杂度
- 时间:O(m·n),每个格子也最多入队 / 出队一次。
- 空间:O(m·n),最坏情况下队列里会装下整块大岛的一大部分。
解法三:并查集(Union-Find,“把陆地分组”)
并查集的思路是“把所有相邻的陆地归成一个集合”:
- 把每个陆地格子
(i, j)看成一个节点,给它一个编号,比如id = i * cols + j。
- 遍历网格,当我们看到一个
'1'时,就看它上面和左边(已经处理过的方向)是不是'1': - 是的话,就把它们做一次
union,表示“这两个格子属于同一座岛”。 - 之所以只看上和左,是因为右和下会在将来的遍历里遇到,不需要重复连。
- 所有 union 完毕后,我们再扫一遍所有
'1',把它们的“根节点”找出来,根的不同个数就是岛的个数。
这种做法像是“分组统计”:每组是一座岛。
复杂度
- 时间:O(m·n) 遍历一次网格 + 若干次
union/find
平均近似 O(m·n · α(mn)),其中
α 是反 Ackermann 函数,极慢,可以认为接近常数,所以几乎是O(m·n)。- 空间:O(m·n),需要为每个陆地格子维护一个并查集节点(parent 映射)。
- 作者:Fan Luo
- 链接:https://fanluo.me/article/leetcode-200-岛屿数量
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
上一篇
[Leetcode 179] 最大数
下一篇
Demystifying Agentic Search Engines