V

算法可视化

ZHEN
分享到微博分享到 XGitHub 仓库个人主页
学习工具算法复杂度速查算法学习路径
数据结构数组链表栈队列树堆哈希表图字典树并查集LRU 缓存跳表线段树B+ 树布隆过滤器树状数组
经典排序算法冒泡排序鸡尾酒排序双调排序选择排序插入排序二分插入排序希尔排序归并排序自顶向下归并快速排序三路快排双轴快排堆排序计数排序基数排序桶排序
图算法Dijkstra 最短路Kruskal 最小生成树Prim 最小生成树Bellman-Ford 最短路拓扑排序Floyd 多源最短路强连通分量2-SAT最大流二分图匹配LCA 倍增欧拉路径
动态规划编辑距离0-1 背包完全背包最长公共子序列最长递增子序列硬币找零方案数石子合并旅行商 TSP树形 DP数位 DP换根 DP
回溯与搜索N 皇后子集生成全排列组合总和迷宫寻路岛屿数量单词搜索数独A* 寻路
字符串KMP 字符串匹配Rabin-KarpBoyer-MooreManacher后缀数组LCP / height 数组AC 自动机Z 函数
数学与数论埃氏筛线性筛欧几里得算法快速幂扩展欧几里得中国剩余定理欧拉函数米勒-拉宾FFTPollard's Rho
计算几何凸包旋转卡壳最近点对线段相交扫描线求交
查找二分查找二分边界旋转数组搜索二分答案三分查找

岛屿数量

回溯与搜索 · Flood Fill

数一数有几片陆地

给一张 0(水)/ 1(陆地)的方格地图,上下左右相连的陆地算作一个岛,问一共有几个岛。它和 迷宫寻路同样是网格搜索,但目标正好互补:迷宫是「从起点找一条路到终点」,岛屿是「扫描整张图、数出有几片连通的陆地」。

扫描 + Flood Fill

做法叫 Flood Fill(洪水填充),就是油漆桶工具的原理:逐格扫描整张网格,遇到一个还没访问过的陆地格,就说明发现了一个新岛——岛屿计数 +1,然后从这一格出发做 DFS,把与它四连通的整片陆地全部「淹掉」(标记为已访问),一片铺满后再继续扫描。已经淹过的陆地和水,扫描时直接跳过。

关键还是「标记已访问」:把数过的陆地沉成水(或另设 visited),才不会把同一个岛重复计数。它和迷宫是同一套网格 DFS 骨架,区别只在——迷宫找到终点就停,岛屿要把每一片都铺满、一直扫到底,最后计数就是答案。

下面是一张 4×4 网格(深色是水、浅色是未访问陆地)。点「下一步」逐步看:🔎 是当前扫描格,遇到新陆地就计数 +1 并 Flood Fill 把整片标绿;数完全图,绿色连通块共 3 片 —— 答案就是 3。右侧代码随每一步同步高亮。

🔎

扫描 (0,0):发现未访问陆地 → 第 1 个岛屿,从这里 Flood Fill

1function numIslands(grid: number[][]): number {
2 const rows = grid.length, cols = grid[0].length;
3 let count = 0;
4 const flood = (r: number, c: number): void => {
5 if (r < 0 || r >= rows || c < 0 || c >= cols) return;
6 if (grid[r][c] === 0) return; // 水 / 已访问
7 grid[r][c] = 0; // 标记为已访问(沉岛)
8 flood(r - 1, c); flood(r + 1, c);
9 flood(r, c - 1); flood(r, c + 1);
10 };
11 for (let r = 0; r < rows; r++) {
12 for (let c = 0; c < cols; c++) {
13 if (grid[r][c] === 1) { // 命中新陆地
14 count++;
15 flood(r, c);
16 }
17 }
18 }
19 return count;
20}
网格4×4(1=陆地/0=水)
当前格(0,0)
岛屿数1
已数陆地1
1 / 20

本页用 DFS 铺岛,也可以用 BFS(队列)一圈圈地淹,效果相同;把「数几片」换成「记录每片的大小」,就能求最大岛屿面积。Flood Fill 是连通分量在网格上的形态。

Flood Fill 在哪里用

图像处理:油漆桶填充、连通域标记、去除小噪点。
游戏:扫雷点开一片空格、消消乐找同色块、地图区域划分。
网格建模:岛屿 / 湖泊 / 势力范围计数,本质都是连通分量。

和 迷宫寻路两页对照着看——同一张网格、同一套 DFS,「找一条路」与「数所有连通块」是网格搜索最常见的一对目标。