DFS走迷宫的C++完整版

  • 知识储备
    • 一:普通动态二维数组的构造
    • 二:栈的构造
    • 三:栈的逆序遍历
  • Main文件代码

该版本代码是配合 懒猫老师用DFS走迷宫视频 基于C++基础所写的完整版(实现了栈逆序遍历),适合小白系统性的学习和复习知识点,只要将下文.h和.cpp和main文件所展示的代码结合起来就是个完整的代码。

知识储备

一:普通动态二维数组的构造

  1. 二维数组中不同行的内存空间不一定连续
  2. 释放内存时,需要先释放各个元素指向的内存,最后释放指针数组名指向的内存(层层释放)
  3. 动态二维数组释放后还会有个数组名地址(指针)的字节符大小
  4. 求数组大小的函数的时候必须将数组引用传递!否则数组会退化为一个指针,无法正确的使用sizeof运算符求出数组所占内存空间大小

下面对动态创建二维数组举例

/** 动态创建 */
int **nums = new int*[10];     //申请了一个int*类型的10行空间
for(int i = 0; i < 10; i++)
{nums[i] = new int[5];     //每一行申请一个int类型的5列空间
}/** 释放空间 */
for(int i = 0; i < 10; i++)delete[] nums[i];      //原则就是层层释放
delete[] nums;

注意:静态二维数组必须是要给定行/列的,如下代码是错误示范

int M, N;
cin >> M, N;
int maze[M+2][N+2];       //这里会报错,因为这是静态数组
// M 和 N尽管你有输入,但是编译器在预编译时是看成变量的,也就是不定的二维数组

二:栈的构造

迷宫问题中构造栈,我通过创建类来实现,具体怎么构造我这就不详细说了(因为这是基本功),.h文件用来存放方法声明,.cpp文件用来实现方法
以下是.h文件的代码(我去掉了析构函数)

struct Direction //构建结构体
{int incX, incY;
};

构建Direction结构体是为了后面构造个结构体数组,该行走方向只有上下左右,没有左上、右上之类的,所以才储存4个方向,该数组储存内容如下图,如incX = 0, incY = 1是为了向右行走(X是横向,Y是纵向)

struct Box   //将每个迷宫格子看成是一个结构体
{int x, y, di;//x和y分别代表迷宫格子的横纵坐标,di为当前的方向
};typedef struct Node       //这是栈节点
{Box data;struct Node* pNext;
}Node, *PNODE;class Stack
{private:PNODE pTop;PNODE pBottom;public:Stack();void initStack(Stack*);void pushStack(Stack*, Box); //将栈中的栈底元素返回并移除,用递归来实现栈逆序遍历的Box getElement(Stack&, Box&);void reverse(Stack&, Box&); void traverse(Stack*);void pop(Stack*, Box&);bool isEmpty(Stack*);
};

以下是.cpp文件代码

#include "Stack.h"
#include <iostream>
#include <stdlib.h>       //exit函数要用到
using namespace std;Stack::Stack()
{//ctor
}void Stack::initStack(Stack* pS)
{pS->pTop = new Node;if(nullptr == pS->pTop){cout << "动态内存分配失败!" << endl;exit(-1);}else{pS->pBottom = pS->pTop;pS->pTop->pNext = nullptr;}return;
}void Stack::pushStack(Stack* pS, Box temp) //temp是将一个结构体整体入栈
{PNODE pNew = new Node;pNew->data = temp;pNew->pNext = pS->pTop;pS->pTop = pNew;
}
/** 这里开始是逆序遍历栈的关键代码,等会详细讲解 */
Box Stack::getElement(Stack& s, Box& temp)
{s.pop(&s, temp);Box res = temp;if(s.isEmpty(&s)) return res;else{Box last = getElement(s, temp);s.pushStack(&s, res);return last;}
}void Stack::reverse(Stack& s, Box& temp)
{if(s.isEmpty(&s)) return;Box i  = getElement(s, temp);s.reverse(s, temp);s.pushStack(&s, i);
}
/****************************************/
void Stack::traverse(Stack* pS)
{PNODE p = pS->pTop;        //p是移动指针,在栈顶和栈底间上下移动的while(p != pS->pBottom){cout << "(" << p->data.x << ", " << p->data.y << ")" << endl;p = p->pNext;}cout << endl;return;
}void Stack::pop(Stack* pS, Box& temp)  //这里的temp一定要 采用引用
{if(isEmpty(pS)) return;else{PNODE r = pS->pTop;temp = r->data;pS->pTop = r->pNext;free(r);r = NULL;return;}
}bool Stack::isEmpty(Stack* pS)
{if(pS->pTop == pS->pBottom)return true;elsereturn false;
}

三:栈的逆序遍历

我这里提供一种递归的思路来实现,当然也可以直接在自己构建栈遍历的方法中改进。栈的逆序遍历不难实现,看图和代码自己尝试下就行。其实这个知识点,也有些题会单独拿出来考,万一你面试的时候遇上这问题呢?~

Box Stack::getElement(Stack& s, Box& temp)
{s.pop(&s, temp);   //这里的temp是引用的,会随变化而变化Box res = temp;        //这里的temp和上面的temp一样//递归结束条件:栈空的时候,返回栈最底下的单元if(s.isEmpty(&s)) return res;  else{Box last = getElement(s, temp);       //一直递归s.pushStack(&s, res);return last;}
}

为了方便理解上面代码,请结合代码和下图来看

这时我们只是把一个栈最底部的单元提出来了,那么怎么接着提取其他呢?那当然可以用递归实现,就可以成功的反转栈了(如下代码)

void Stack::reverse(Stack& s, Box& temp) //这里的temp也要用引用
{if(s.isEmpty(&s)) return;Box i  = getElement(s, temp);s.reverse(s, temp);s.pushStack(&s, i);
}

也是结合代码来看下图理解吧,这里递归的reversepushStack可能比较难理解,简单来说就是进行一次reverse就含有一个pushStack,进行两次reverse就含有两个pushStack····直到栈空,每次return(有很多次,取决于栈元素的个数)前都会执行pushStack

Main文件代码

#include <iostream>
#include "Stack.h"using namespace std;bool findPath(Stack& s, int M, int N)
{int **maze = new int*[M+2];      //动态构造二维数组for(int i = 0; i < 10; i++){maze[i] = new int[N+2];}for(int i = 0; i < M+2; i++){  //动态给定迷宫for(int j = 0; j < N+2; j++){if(i == 0 || i == M+1)maze[i][j] = 1;else if(j == 0 || j == N+1)maze[i][j] = 1;elsecin >> maze[i][j];}}Direction direct[4];       //方向数组(文章上面有图有说到)direct[0].incX = 0; direct[0].incY = 1;direct[1].incX = 1; direct[1].incY = 0;direct[2].incX = 0; direct[2].incY = -1;direct[3].incX = -1; direct[3].incY = 0;Box temp;int x, y, di;      //当前正在处理的迷宫格子int line, col;    //迷宫格子预移方向后,下一格子的行坐标、列坐标maze[1][1] = -1;temp.x = 1;temp.y = 1;temp.di = -1;      //起始点直接设置为-1代表该格子已访问过了s.pushStack(&s, temp);while(!s.isEmpty(&s)){s.pop(&s, temp);  //这里对遇到走不通的格子进行回退时起到关键作用x = temp.x; y = temp.y; di = temp.di+1;while(di < 4){  //走不通时,四个方向都尝试一遍line = x + direct[di].incX;col = y + direct[di].incY;if(maze[line][col] == 0){   //代表 预走 的格子可以走temp.x = x; temp.y = y; temp.di = di;s.pushStack(&s, temp);x = line; y = col; maze[line][col] = -1; //标为-1是为了表明该格子已经走过,回溯时不再处理if(x == M && y == N){s.reverse(s, temp);return true;   //迷宫有路}else di = 0;}else di++;}}return false;       //迷宫无路
}int main()
{int M, N;bool res;cout << "请输入迷宫数组行数和列数:";cin >> M >> N;Stack s;s.initStack(&s);res = findPath(s, M, N);cout << boolalpha << res << endl;    //将bool转换为true/false显示cout << endl;if(res)s.traverse(&s);elsecout << "你被困在迷宫中了,等着受死吧!" << endl;return 0;
}

  ------------------以上是懒猫老师自构Stack的版本--------------------
  我们都知道C++提供了STL库,那么我们为何不用自带的stack更方便简洁呢?所以为了方便大家用C++,决定在下面开源个用STL库里stack的代码版本,同样也是实现了栈逆序遍历。

#include <bits/stdc++.h>
using namespace std;struct Direction
{int incX, incY;
};struct Box
{int x, y, di;
};Box getElement(stack<Box>& s)
{Box res = s.top();s.pop();if(s.empty()) return res;else{Box last = getElement(s);s.push(res);return last;}
}void reverseStack(stack<Box>& s)
{if(s.empty()) return;Box i = getElement(s);reverseStack(s);s.push(i);
}bool findPath(stack<Box>& s, int M, int N)
{//动态构建二维数组 new 出来的空间不一定是连续的int** maze = new int*[M+2];for(int i = 0; i < N + 2; i++)maze[i] = new int[N+2];for(int i = 0; i < M+2; i++){for(int j = 0; j < N+2; j++){if(i == 0 || i == M+1)maze[i][j] = 1;else if(j == 0 || j == N+1)maze[i][j] = 1;else cin >> maze[i][j];}}Direction direct[4];direct[0].incX = 0; direct[0].incY = 1;direct[1].incX = 1; direct[1].incY = 0;direct[2].incX = 0; direct[2].incY = -1;direct[3].incX = -1; direct[3].incY = 0;Box temp;int x, y, di;       //当前处理的单元格int row, col;     //走迷宫下一该走的单元格maze[1][1] = -1;temp.x = 1;temp.y = 1;temp.di = -1;s.push(temp);while(!s.empty()){x = s.top().x;  y = s.top().y;  di = s.top().di + 1;s.pop();while(di < 4){row = x + direct[di].incX;  col = y + direct[di].incY;if(maze[row][col] == 0){temp.x = x;  temp.y = y;  temp.di = di;s.push(temp);x = row;  y = col;  maze[row][col] = -1;if(x == M && y == N){reverseStack(s);return true;}else di = 0;}else di++;}}return false;
}void traverse(stack<Box> s)
{while(!s.empty()){Box temp = s.top();s.pop();cout << "(" << temp.x << ", " << temp.y << ")" << endl;}return;
}int main()
{int M, N;bool res;cout << "请输入迷宫数组的行数和列数:";cin >> M >> N;stack<Box> s;res = findPath(s, M, N);cout << boolalpha << res << endl;cout << endl;if(res)traverse(s);elsecout << "你被困在迷宫中了" << endl;return 0;
}

Input
8 8
0 0 1 0 0 0 1 0
0 0 1 0 0 0 1 0
0 0 0 0 1 1 0 0
0 1 1 1 0 0 0 0
0 0 0 1 0 0 0 0
0 1 0 0 0 1 0 0
0 1 1 1 0 1 1 0
1 0 0 0 0 0 0 0
  
Output
(1, 1)
(1, 2)
(2, 2)
(3, 2)
(3, 1)
(4, 1)
(5, 1)
(5, 2)
(5, 3)
(6, 3)
(6, 4)
(6, 5)
(7, 5)
(8, 5)
(8, 6)
(8, 7)
  
--------------------------------------------------------
2022年2月24号更新:
  算法竞赛的DFS走迷宫(自定义起始和结束点,打印出所有路径)

DFS走迷宫(懒猫老师C++完整版)相关推荐

  1. DFS走迷宫问题(非最短路径)

    上面之所以要强调非最短路径,是因为在下一篇我要用BFS来求解最短路径的问题.这里只是讲如何走出迷宫. #include<cstdio> #define M 10 #define N 10 ...

  2. 用计算机弹一首学猫叫的歌曲,抖音学猫叫简谱完整版

    <学猫叫>是目前各个娱乐视频平台中非常火爆的一首歌,你想不想用带声音计算器弹奏出一首<学猫叫>呢?学猫叫计算机简谱为你提供了<学猫叫>的计算器简谱,这样你也可以在抖 ...

  3. 埃罗芒阿老师计算机谱,[B型]ヒトリゴト-埃罗芒阿老师OP 完整版

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 00 2567656 787878 789 6565 00 2567656 787878 789 6565 5 3457767 8 678 ++ 986 ...

  4. DFS和BFS概念及实践+acwing 842 排列数字(dfs) +acwing 844. 走迷宫(bfs)

    DFS (深搜), 也有说就是递归的 执着: 一直搜到底,然后回溯下一个节点 数据结构 : stack (这里的栈,实际上是编译器内部的栈, 所以说也可以看成递归, 递归内部也是调用编译器内部栈) 空 ...

  5. 里奥老师乾坤大挪移—深入浅出走迷宫问题之BFS算法1

    还是要从经典的走迷宫问题开始 DFS的思路是只选择一个方向,一条路走到黑,不撞南墙不回头. 而BFS的思路,每一步在走之前,我都要把所有的选择,所有的方向全看一遍,一层一层的往外扩展. 显然DFS比B ...

  6. K - 老鼠走迷宫(DFS)

    Description 现在一只老鼠被困在了迷宫里!你需要判断老鼠能否走出迷宫. 老鼠只能向上下左右四个方向移动.我们认为只要老鼠走到了迷宫的边界即算走出迷宫. Input 第一行输入两个整数 n, ...

  7. DFS(入门题,走迷宫)

    1185: 走迷宫 Time Limit: 1 Sec Memory Limit: 128 MB Submit: 383 Solved: 155 [Submit][Status][Web Board] ...

  8. 【华为机试】【校招】【Java】机器人走迷宫(DFS)

    ■ 题目描述 [机器人走迷宫] 机器人走一个迷宫,给出迷宫的x和y(x*y的迷宫)并且迷宫中有障碍物,输入k表示障碍物有k个,并且会将障碍物的坐标挨个输入. 机器人从0,0的位置走到x,y的位置并且只 ...

  9. 用Python写一个走迷宫的小程序(图形化:matplotlib,dfs,prim)

    不要脸的放到了Github上面,嘿嘿. Github:https://github.com/Radium1209/Maze 先看一下动态效果图(慢放): 首先生成迷宫: 主要用了两个算法:Prim和d ...

  10. 洛谷 P1238 走迷宫【搜索】【DFS】

    洛谷 P1238 走迷宫 一.题目链接 二.题目分析 (一)算法标签 (二)解题思路 三.AC代码 四.其它题解 一.题目链接 洛谷 P1238 走迷宫 二.题目分析 (一)算法标签 搜索 DFS ( ...

最新文章

  1. easyui accordion全部是关闭状态
  2. perl pop和push函数,不使用索引更能利用perl的特性,减少边界值错误发生的几率...
  3. php 面向对象 教程,PHP学习笔记之面向对象设计_PHP教程
  4. java的地位和优势,Java语言之所以能持续占领霸主地位 这些优势功不可没
  5. 7、编译安装LAMP之apache与PHP整合
  6. IP地址归属地-ip离线库
  7. c语言通讯录人数显示,c语言实现通讯录
  8. 《黑客X档案》2006年-2012年全集(PDF格式)
  9. SS服务未启动,核心功能不可用的解决办法
  10. excel多个工作表合并怎么操作
  11. 安泰测试-安捷伦N5182A射频矢量信号发生器
  12. Selenium控制已打开的IE浏览器
  13. cscd期刊计算机排名,计算机CSCD核心期刊.pdf
  14. 精工机械表 调整时间,日期和星期的方法
  15. 利用OneDNS同步chrome数据
  16. android引用外部字体
  17. 手机红外遥控器 求源码
  18. 反对知识霸权 (转)
  19. 处理极端情况:财务扩展和流式传输
  20. lattepanda安装linux系统,LattePandaAI-Linux系统环境部署

热门文章

  1. Intel 处理器发展年历
  2. 数据结构严蔚敏--综述
  3. 饭饭的Selenium+xlwt笔记
  4. GIS行业地图绘制的基本要求
  5. 静态小米官网首页仿站笔记
  6. Snapchat面试题:移除K位
  7. 移动端屏幕适配(750px设计稿)
  8. ω-3脂肪酸:事实的列举
  9. qmainwindow 标题栏_Qt:自定义标题栏(QMainWindow)
  10. Firefox的下载经管器:FlashGot v1.0 Final发布