dfs遍历二维数组找连通区域最直接,本质是值相同且四邻接的格子组成的块;需注意边界检查、重复访问标记、方向数组设计及递归栈溢出风险。

用 DFS 遍历二维数组找连通区域最直接
连通区域本质是值相同(比如都是 1)且上下左右相邻的格子组成的块。C++ 里没有内置“连通区域检测”,得自己写搜索逻辑,dfs 是最自然的选择——递归或栈实现都行,但递归写起来快,注意别爆栈就行。
常见错误是漏判边界或重复访问:没检查 x 、<code>y 、<code>x >= rows、y >= cols 就直接访问数组,结果越界崩溃;或者不标记已访问位置,导致无限递归或重复计数。
实操建议:
- 用一个与原数组同尺寸的
vector<vector>></vector>标记是否访问过,或者直接在原数组上改值(如把1改成-1),省空间但会破坏原始数据 - 方向数组写成
vector<pair>> dirs = {{0,1},{1,0},{0,-1},{-1,0}};</pair>,比硬写四次 if 清晰还不易错 - 递归函数参数传引用:比如
void dfs(vector<vector>>& grid, int x, int y, vector<vector>>& visited)</vector></vector>,避免拷贝二维数组
用 BFS 替代 DFS 更稳,尤其对大区域
DFS 深度可能达到 O(m×n),在栈空间小的环境(比如某些嵌入式编译器或竞赛限制)容易 stack overflow;BFS 借助 queue 把深度转成内存开销,更可控。
典型使用场景:图像分割预处理、游戏地图中可通行区域统计、扫雷展开逻辑。
实操建议:
- 用
queue<pair>></pair>存坐标,每次取队首,向四个方向扩展,合法且未访问就入队 - 入队前立刻标记
visited[x][y] = true,否则同一格子可能被多个邻居重复加入队列 - 如果只关心区域数量(不关心每个区域大小),BFS 外层循环一次就对应一个连通块
处理不同连通定义:八连通 vs 四连通
默认说“上下左右”是四连通;如果要求“斜向也算连通”(比如像素级图像分析),就得改成八连通——方向数组加四个对角:{1,1}、{1,-1}、{-1,1}、{-1,-1}。
性能影响明显:八连通分支因子从 4 升到 8,单次 BFS/DFS 扩展量翻倍,但实际运行时间差异通常不大,除非网格极大且连通块极多。
注意点:
- 八连通下,
(0,0)和(1,1)算连通,但(0,0)和(1,2)不算——别误以为所有非零距离都行 - 有些业务场景(如棋盘游戏)明确禁止斜向移动,硬套八连通会导致逻辑错误
- 如果输入是
char或bool类型数组,记得比较时用对应字面量,比如grid[x][y] == '1'或grid[x][y],别漏类型转换
LeetCode 200 题那种“岛屿数量”可以直接套模板
200. Number of Islands 就是最典型的二维数组连通区域计数问题:字符矩阵里 '1' 是陆地,'0' 是水,求岛屿数。它不关心区域形状或大小,只数块数。
这个场景下,核心就是外层双重循环找第一个未访问的 '1',然后启动一次 DFS/BFS 把整块淹掉(或标记),计数器加一。
容易被忽略的细节:
- 输入可能是空矩阵:
grid.empty() || grid[0].empty()得先判,不然grid[0].size()会崩 - C++ 的
vector是行优先,grid[i][j]中i是行号、j是列号,别和数学坐标系混淆 - 如果用 BFS 且需要返回每个区域的坐标列表,别在循环里反复
clear()临时容器,用移动语义或作用域控制生命周期更安全
&& y 就段错误,多一个 <code>= 就逻辑错。与其靠记忆,不如把边界检查封装成内联函数,比如 inline bool valid(int x, int y) { return x >= 0 && x = 0 && y ,后面所有地方调用它。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











