• (迷宫问题)DFS(递归+非递归)


    目录

    介绍

    步骤 

    应用 

    遍历图/树

    查找路径/解决问题

    迷宫

    非递归版本

    思路

    代码

    递归版本

    思路

    代码


    介绍

    深度优先搜索(Depth-First Search,DFS)是一种用于图和树等数据结构中的遍历或搜索算法

    它通过尽可能地探索图的分支,直到不能再继续为止,然后回溯并继续探索其他分支

    步骤 

    • 从起始节点开始,标记该节点为已访问
    • 探索从当前节点出发的每个相邻节点
    • 对于每个相邻节点,如果它还没有被访问,就递归地访问它,然后继续深度探索
    • 如果没有未访问的相邻节点,或者达到终止条件(自己设置的,例如找到目标节点或遍历完整个图),则回溯到上一个节点,继续查找未访问的节点
    • 重复以上步骤,直到所有节点都被访问或找到了解决问题的路径

    应用 

    遍历图/树

    • DFS 可以用来访问并处理图或树中的所有节点
    • 通过深度优先的方式,它会首先访问一个节点,然后递归地访问其相邻节点,以此类推,直到所有节点都被访问

    查找路径/解决问题

    • DFS 可以用于查找从一个节点到另一个节点的路径
    • 或者用于解决搜索问题,如迷宫问题、拓扑排序、生成树等

    迷宫

    下面用迷宫作为例子,展示一下如何用递归/非递归版本的dfs解决迷宫问题 

    首先,先定义迷宫

    1. // 定义迷宫的大小
    2. const int m = 5;
    3. const int n = 5;
    4. // 定义方向,上、右、下、左
    5. int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };
    6. // 定义三元组结构体
    7. struct Triple {
    8. int x, y, dir; //横坐标,纵坐标,下一个位置的方向
    9. Triple(int _x, int _y, int _dir)
    10. : x(_x), y(_y), dir(_dir) {}
    11. };

    这里,还引入了一个新变量,dir -- 记录下一个位置的方向(别问为什么,问就是老师布置的作业中有这个)

    非递归版本

    思路

    虽然是迷宫问题,但实际上其实就是找能走的位置,然后将它记录下来

    • 而我们dfs是需要回溯的,也就是会将最后一个元素删掉,而且记录元素都是插在尾巴上
    • 所以!就是我们最好的选择

    由于我们要记录下一个位置的方向,但是我们又无法提前预知到

    • 虽然下一个方向的不知道,但我们可以知道这次要选择的方向(也就是上一个位置的下一个方向)
    • 所以,存储的时候,我们要插入两次
    • 一次是上一个位置+它对应的下一个方向(这个是我们最终路径上有效的值)
    • 一次是这次的位置+随便一个值(因为这个是为了让我们取位置用的,方向没用到)
    • 注意第二次插入的临时值,在下次探索的时候,需要先pop掉,再进行操作

    还有就是,我们这里存进去的下标都是从1开始的,但实际使用的时候需要-1,因为计算机里的下标是从0开始的,所以会有相互转换的处理

    代码

    1. // 非递归DFS算法
    2. bool solveMaze(stack& s, int maze[m][n],int sx, int sy, int tx, int ty) {
    3. s.push(Triple(sx, sy, 0));
    4. maze[sx-1][sy-1] = 2;
    5. while (!s.empty()) {
    6. Triple cur = s.top();
    7. int x = cur.x - 1, y = cur.y - 1, d = 0;
    8. if (x == tx - 1 && y == ty - 1) {
    9. return true;
    10. }
    11. bool found = false;
    12. while (d < 4) {
    13. int nx = x + dir[d][0];
    14. int ny = y + dir[d][1];
    15. if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] == 0) {
    16. s.pop(); //说明上一个位置有效,所以先把那个临时值pop掉
    17. found = true;
    18. s.push(Triple(x+1, y+1, d)); //有效值
    19. s.push(Triple(nx + 1, ny + 1, d)); //临时值
    20. x = nx;
    21. y = ny;
    22. maze[x][y] = 2; // 标记为已访问过的路径
    23. break;
    24. }
    25. else {
    26. d++; // 尝试下一个方向
    27. }
    28. }
    29. if (found == false) {
    30. s.pop(); //四个方向都不行,说明这个位置不对
    31. }
    32. }
    33. return false; // 没有找到通路
    34. }

    递归版本

    因为递归版本老师要求我们求出所有通路,所以,元素结构也要变一下,不需要记录那个方向了

    1. struct Path {
    2. vectorint, int>> steps;
    3. };

    思路

    还是一样的思路

    每次取上一个位置的坐标进行四个方向的探索,如果可以,就把这个结点插入,并且以该结点为基础进行下一次的探索

    代码

    1. int a[m][n]; //用于计算类型(其实可以不用的(挠头))
    2. void findPaths(Path& currentPath,decltype(a)maze, int sx, int sy, int tx, int ty) {
    3. if (sx == tx && sy == ty ) {
    4. // 找到一条通路,输出路径
    5. cout << "通路:";
    6. for (const auto& step : currentPath.steps) {
    7. cout << "(" << step.first << "," << step.second << ") ";
    8. }
    9. cout << endl;
    10. currentPath.steps.pop_back(); //把终点pop掉
    11. return;
    12. }
    13. for (int d = 0; d < 4; d++) {
    14. int size = currentPath.steps.size();
    15. if (size == 0) {
    16. break;
    17. }
    18. int x = currentPath.steps[size - 1].first, y = currentPath.steps[size - 1].second;
    19. int nx = x + dir[d][0]-1;
    20. int ny = y + dir[d][1]-1;
    21. if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] == 0) {
    22. // 标记当前位置已访问
    23. maze[nx][ny] = 2;
    24. // 添加当前步骤到路径
    25. currentPath.steps.push_back(make_pair(nx+1, ny+1));
    26. // 继续搜索下一步
    27. findPaths( currentPath,maze, nx+1, ny+1, tx,ty);
    28. // 恢复状态,回溯
    29. if (currentPath.steps.size() != 0) {
    30. currentPath.steps.pop_back();
    31. }
    32. maze[nx][ny] = 0;
    33. }
    34. }
    35. }

  • 相关阅读:
    【手把手带你学JavaSE】全方面带你了解异常
    speaker-test报错问题解决方法
    【Python零基础入门篇 · 7】:列表、元组的相关操作(完整版)
    栈的实现及OJ练习(c语言)
    社交创新:Facebook的技术与产品发展
    在taro开发小程序中,创建全局事件,更新各个tabbar页面数据,适用购物车更新,taro购物车数据同步
    Multi-series Time-aware Sequence Partitioning for Disease Progression Modeling
    新茶饮进入“大逃杀”赛程
    vivo 自研Jenkins资源调度系统设计与实践
    数据结构--6.3查找算法(静态、动态)(插值查找)
  • 原文地址:https://blog.csdn.net/m0_74206992/article/details/133753590