当前位置: 首页 > news >正文

c++算法学习3——深度优先搜索

一、深度优先搜索的核心概念

DFS算法是一种通过递归或栈实现的"一条路走到底"的搜索策略,其核心思想是:

  1. 深度优先:从起点出发,选择一个方向探索到底,直到无路可走

  2. 回溯机制:遇到死路时返回最近的分叉点尝试其他路径

  3. 状态标记:记录已访问位置,避免重复访问

二、迷宫问题的DFS解法框架
1. 题目引入:

给定一个 n×n 的迷宫矩阵,判断是否存在从左上角 (0,0) 到右下角 (n-1,n-1) 的通路。移动规则如下:

  1. ​移动方向​​:每次只能向 ​​上、下、左、右​​ 四个方向移动一格
  2. ​通行规则​​:
    • . 表示可通行的路径
    • # 表示不可通行的墙壁
  3. ​特殊条件​​:
    • 若入口 (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");
算法特点
  1. 不需要回溯:找到一条路径即可停止

  2. 不恢复访问标记:已探索区域无需重复访问

  3. 空间优化:使用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²) 由递归深度决定

http://www.xdnf.cn/news/891631.html

相关文章:

  • 【java面试】框架篇
  • snprintf函数用法及注意事项详解
  • Redisson简明教程—你家的锁芯该换了
  • 71 LV信息查看
  • DeepSeek私有化部署的理性抉择:谁需要?谁不必?
  • SSH 和 Telnet 介绍、区别与使用方法
  • JAVA-springboot JUnit单元测试
  • Qt实现一个悬浮工具箱源码分享
  • LeetCode_LCR 509 斐波拉契
  • 经济学顶刊QJE:构建从非结构化文本数据中挖掘经济规律的新框架!
  • 【QT】qtdesigner中将控件提升为自定义控件后,css设置样式不生效(已解决,图文详情)
  • 实测报告:设备 AI 知识库如何帮助新手快速掌握巡检技巧?
  • 在嵌入式中C语言中static修饰的变量常量和字符串常量存储位置
  • 总结vxe-grid的一些用法
  • 精度分析方法-不确定度
  • [蓝桥杯]三体攻击
  • MySQL的并发事务问题及事务隔离级别
  • 12V降5V12A大功率WD5030A,充电器、便携式设备、网络及工业领域的理想选择
  • 大语言模型评测体系全解析(中篇):专项能力评测与行业垂直场景
  • Mysql莫名奇妙重启
  • 实现单例模式的常见方式
  • Redis Set集合命令、内部编码及应用场景(详细)
  • GC1809:高性能音频接收与转换芯片
  • Python Day42 学习(日志Day9复习)
  • AI智能推荐实战之RunnableParallel并行链
  • .Net Framework 4/C# System.IO 命名空间(文件的输入输出)
  • 深度学习之模型压缩三驾马车:基于ResNet18的模型剪枝实战(2)
  • 箭头函数和普通函数的this指向
  • BLE中心与外围设备MTU协商过程详解
  • 炫云:为驱动数字视觉产业升级保驾护航