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

目录

介绍

步骤 

应用 

遍历图/树

查找路径/解决问题

迷宫

非递归版本

思路

代码

递归版本

思路

代码


介绍

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

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

步骤 

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

应用 

遍历图/树

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

查找路径/解决问题

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

迷宫

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

首先,先定义迷宫

// 定义迷宫的大小
const int m = 5;
const int n = 5;

// 定义方向,上、右、下、左
int dir[4][2] = { {-1, 0}, {0, 1}, {1, 0}, {0, -1} };

// 定义三元组结构体
struct Triple {
    int x, y, dir; //横坐标,纵坐标,下一个位置的方向
    Triple(int _x, int _y, int _dir) 
        : x(_x), y(_y), dir(_dir) {}
};

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

非递归版本

思路

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

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

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

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

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

代码

// 非递归DFS算法
bool solveMaze(stack<Triple>& s, int maze[m][n],int sx, int sy, int tx, int ty) {
    s.push(Triple(sx, sy, 0));
    maze[sx-1][sy-1] = 2;

    while (!s.empty()) {
        Triple cur = s.top();
        int x = cur.x - 1, y = cur.y - 1, d = 0;

        if (x == tx - 1 && y == ty - 1) {
            return true;
        }

        bool found = false;
        while (d < 4) {
            int nx = x + dir[d][0];
            int ny = y + dir[d][1];

            if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] == 0) {
                s.pop(); //说明上一个位置有效,所以先把那个临时值pop掉
                found = true;
                s.push(Triple(x+1, y+1, d));  //有效值
                s.push(Triple(nx + 1, ny + 1, d)); //临时值
                x = nx;
                y = ny;
                maze[x][y] = 2;  // 标记为已访问过的路径
                break;
            }
            else {
                d++;  // 尝试下一个方向
            }
        }
        if (found == false) {
            s.pop(); //四个方向都不行,说明这个位置不对
        }
    }

    return false;  // 没有找到通路
}

递归版本

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

struct Path {
    vector<pair<int, int>> steps;
};

思路

还是一样的思路

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

代码

int a[m][n]; //用于计算类型(其实可以不用的(挠头))
void findPaths(Path& currentPath,decltype(a)maze, int sx, int sy, int tx, int ty) {
    if (sx == tx && sy == ty ) {
        // 找到一条通路,输出路径
        cout << "通路:";
        for (const auto& step : currentPath.steps) {
            cout << "(" << step.first << "," << step.second << ") ";
        }
        cout << endl;
        currentPath.steps.pop_back(); //把终点pop掉
        return;
    }


    for (int d = 0; d < 4; d++) {
        int size = currentPath.steps.size();
        if (size == 0) {
            break;
        }
        int x = currentPath.steps[size - 1].first, y = currentPath.steps[size - 1].second;
        int nx = x + dir[d][0]-1;
        int ny = y + dir[d][1]-1;

        if (nx >= 0 && nx < m && ny >= 0 && ny < n && maze[nx][ny] == 0) {
            // 标记当前位置已访问
            maze[nx][ny] = 2;

            // 添加当前步骤到路径
            currentPath.steps.push_back(make_pair(nx+1, ny+1));

            // 继续搜索下一步
            findPaths( currentPath,maze, nx+1, ny+1, tx,ty);

            // 恢复状态,回溯
            if (currentPath.steps.size() != 0) {
                currentPath.steps.pop_back();
            }
            maze[nx][ny] = 0;
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值