c++算法学习3——深度优先搜索
一、深度优先搜索的核心概念
DFS算法是一种通过递归或栈实现的"一条路走到底"的搜索策略,其核心思想是:
-
深度优先:从起点出发,选择一个方向探索到底,直到无路可走
-
回溯机制:遇到死路时返回最近的分叉点尝试其他路径
-
状态标记:记录已访问位置,避免重复访问
二、迷宫问题的DFS解法框架
1. 题目引入:
给定一个 n×n 的迷宫矩阵,判断是否存在从左上角 (0,0)
到右下角 (n-1,n-1)
的通路。移动规则如下:
- 移动方向:每次只能向 上、下、左、右 四个方向移动一格
- 通行规则:
.
表示可通行的路径#
表示不可通行的墙壁
- 特殊条件:
- 若入口
(0,0)
或出口(n-1,n-1)
为墙壁(#
),直接判定为不可达 - 路径不可走出迷宫边界
- 若入口
输入格式
- 第1行:正整数
n
(1 ≤ n ≤ 100),表示迷宫规模 - 后续n行:每行包含
n
个字符(.
或#
),用空格分隔,表示迷宫矩阵
示例输入:
4
. # . .
. . # .
# . . .
. . . .
输出格式:
- 若存在路径,输出
YES
- 否则输出
NO
示例输出:
YES
2. 题目分析:
在走迷宫时,面临四个选择方向,我们可以按顺时针方向依次尝试。
首先观察右边,若可行则走向右边,并按相同方法继续探索;
接着查看下方,若可行则走向下方,再按相同方法探索;
然后查看左边,若可行则走向左边,继续按相同方式探索;
最后查看上方,若可行则走向上方,并继续以相同方式前进。
3.需要准备的工作
const int N = 110; // 迷宫最大尺寸
char maze[N][N]; // 存储迷宫地图
bool visited[N][N]; // 标记访问状态
int n; // 迷宫尺寸
// 方向数组:右→下→左→上(顺时针)
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
4. DFS核心模板
void dfs(int x, int y) {// 1. 边界判断:到达终点if (x == n-1 && y == n-1) {success = true;return;}// 2. 尝试四个方向for (int i = 0; i < 4; i++) {int nx = x + dx[i], ny = y + dy[i];// 3. 检查新位置有效性if (nx >=0 && nx <n && ny>=0 && ny<n && !visited[nx][ny] && maze[nx][ny]=='.') {visited[nx][ny] = true; // 标记已访问dfs(nx, ny); // 递归探索// visited[nx][ny] = false; // 回溯时需要取消标记}} }
三、无回溯DFS:判断路径存在性
问题要求:只判断能否从入口(0,0)走到出口(n-1,n-1)
关键实现:
// 初始化:标记障碍物 for (int i=0; i<n; i++) {for (int j=0; j<n; j++) {if (maze[i][j] == '#') visited[i][j] = true;} }// 检查入口/出口有效性 if (maze[0][0]=='#' || maze[n-1][n-1]=='#') {cout << "NO";return 0; }// 启动DFS visited[0][0] = true; dfs(0, 0);// 输出结果 cout << (success ? "YES" : "NO");
算法特点
-
不需要回溯:找到一条路径即可停止
-
不恢复访问标记:已探索区域无需重复访问
-
空间优化:使用
visited
数组避免循环
四、有回溯DFS:统计路径数量
问题变体:我们把最开始的问题改变一下,不求能否通过迷宫,而是计算从入口到出口的所有能通行的路径数量。
关键修改:
int pathCount = 0; // 全局路径计数器void dfs(int x, int y) {if (x == n-1 && y == n-1) {pathCount++; // 找到一条路径return;}for (int i=0; i<4; i++) {int nx = x + dx[i], ny = y + dy[i];if (nx>=0 && nx<n && ny>=0 && ny<n && !visited[nx][ny] && maze[nx][ny]=='.') {visited[nx][ny] = true; // 标记占用dfs(nx, ny);visited[nx][ny] = false; // 关键回溯:释放位置}} }
回溯机制图解
起点(0,0) ├─ 右→ (0,1) → ... → 终点 → 回溯 ├─ 下→ (1,0) → ... → 终点 → 回溯 ├─ 左→ 无效 └─ 上→ 无效
每次递归返回时取消标记,允许其他路径使用该位置
五、DFS执行过程分析
栈结构模拟(以3x3迷宫为例):
步骤 | 栈状态 | 当前动作 -----|---------------|----------- 1 | (0,0) | 探索右侧(0,1) 2 | (0,0),(0,1) | 探索下方(1,1) 3 | (0,0),(0,1) | (1,1)是死路,回溯 4 | (0,0) | 探索下方(1,0) 5 | (0,0),(1,0) | 到达终点(2,2)
时间复杂度:O(4^n) 最坏情况(指数级)
空间复杂度:O(n²) 由递归深度决定