1. 深度搜索(DFS)的本质与核心思想
深度优先搜索(Depth-First Search)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支,直到无法继续为止,然后回溯到上一个节点继续搜索其他分支。
我第一次接触DFS是在解决迷宫问题时。当时尝试用递归方法走出迷宫,发现程序总是沿着一条路走到黑,碰壁后再回退寻找新路径——这正是DFS的核心特征。与广度优先搜索(BFS)不同,DFS更注重"深度"而非"广度",这种特性使其在解决某些问题时特别高效。
DFS通常通过递归或显式栈来实现。递归实现最为直观,代码简洁但可能面临栈溢出风险;显式栈实现稍复杂但更可控。在C++中,我们常用vector或stack作为显式栈的数据结构。
关键理解:DFS的"深度优先"特性使其天然适合解决需要探索所有可能性的问题,如排列组合、连通性检测等。它的回溯特性也是解决许多难题的关键。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. DFS的递归实现框架与关键技术点
2.1 基础递归模板
一个标准的DFS递归实现包含三个关键部分:
- 终止条件(何时停止递归)
- 当前层处理(如何处理当前节点)
- 递归调用(如何进入下一层)
cpp复制void dfs(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层节点处理) {
处理节点;
dfs(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
2.2 方向数组的使用技巧
在网格类问题中,方向数组是DFS的得力助手。以下是一个经典的4方向数组定义:
cpp复制const int dirX[4] = {0, 0, -1, 1}; // 左右移动
const int dirY[4] = {-1, 1, 0, 0}; // 上下移动
使用时配合边界检查,可以优雅地实现网格遍历:
cpp复制for (int i = 0; i < 4; i++) {
int newX = x + dirX[i];
int newY = y + dirY[i];
if (newX >= 0 && newX < rows && newY >= 0 && newY < cols) {
dfs(grid, newX, newY);
}
}
2.3 访问标记的多种实现方式
防止重复访问是DFS的关键,常见标记方法有:
- 修改原数据(如将访问过的格子设为0)
- 使用独立的visited数组
- 位图标记(适用于空间敏感场景)
- 哈希表存储(适用于非连续节点)
在图像渲染问题中,我们采用了第一种方法:
cpp复制if (image[sr][sc] == curColor) {
image[sr][sc] = color; // 修改原数据作为标记
// 继续DFS...
}
3. 典型DFS例题精解
3.1 图像渲染问题(Flood Fill)
LeetCode 733题是DFS的经典应用。问题描述:给定一个二维数组表示的图像,从指定像素开始,将所有颜色相同的连通区域染成新颜色。
cpp复制class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int oldColor = image[sr][sc];
if (oldColor != color) dfs(image, sr, sc, oldColor, color);
return image;
}
void dfs(vector<vector<int>>& image, int x, int y, int oldColor, int newColor) {
if (x < 0 || x >= image.size() || y < 0 || y >= image[0].size() ||
image[x][y] != oldColor) return;
image[x][y] = newColor;
dfs(image, x+1, y, oldColor, newColor);
dfs(image, x-1, y, oldColor, newColor);
dfs(image, x, y+1, oldColor, newColor);
dfs(image, x, y-1, oldColor, newColor);
}
};
实际调试中发现:当新旧颜色相同时必须直接返回,否则会导致无限递归。这是DFS实现中常见的边界条件陷阱。
3.2 岛屿最大面积问题
LeetC
