以下部分内容来自于B站视频 – 麦克老师讲算法
视频链接:https://www.bilibili.com/video/BV1bK4y1C7W2?p=2&spm_id_from=pageDriver.
迷宫示例图像如下:
程序实现使用了递归思想,没有用到堆栈知识。
实现的代码:
#include <iostream>
using namespace std;
/*
* 由于这里使用数组的原因, 而数组的初始值是为0的,
* 所以不能让 0 表示空地, 1 表示障碍物。
* 这里用 1 表示空地, 2 表示障碍物。
*/
int start_x, start_y; //起点坐标:(start_x, start_y)
int end_x, end_y; //终点坐标:(endx, endy)
int min_step = 99999;//一开始给它一个很大的数
//地图,放100行100列
int map[100][100];//1表示空地,2表示障碍物
int visit[100][100];//1表示访问,0表示未访问
void dfs(int x,int y,int step)
{
if (x== end_x && y== end_y)
{
//如果当前步数小于最小值,就更新
if (step < min_step)
min_step = step;
return;//回溯
}
//顺时针试探
//右
//如果当前位置的右边为空地且没有访问过
if (map[x][y + 1] == 1 && visit[x][y + 1] == 0)
{
//将右边的点设置为已访问
visit[x][y + 1] = 1;
dfs(x, y + 1, step + 1);
visit[x][y + 1] = 0;
}
//下
if (map[x+1][y] == 1 && visit[x+1][y] == 0)
{
//将下边的点设置为已访问
visit[x+1][y] = 1;
dfs(x + 1, y, step + 1);
visit[x+1][y] = 0;
}
//左
if (map[x][y - 1] == 1 && visit[x][y - 1] == 0)
{
//将左边的点设置为已访问
visit[x][y - 1] = 1;
dfs(x, y - 1, step + 1);
visit[x][y - 1] = 0;
}
//上
if (map[x-1][y] == 1 && visit[x-1][y] == 0)
{
//将上边的点设置为已访问
visit[x-1][y] = 1;
dfs(x-1, y, step + 1);
visit[x-1][y] = 0;
}
return;
}
int main()
{
int m, n;
printf("请设置地图的行数和列数,以逗号分隔:\n");
scanf("%d,%d",&m,&n);
printf("请输入 %d 行 %d 列的地图:\n",m,n);
for (int i=1;i<=m;i++)
{
for (int j = 1; j <= n; j++)
scanf("%d",&map[i][j]);//0表示空地,1表示障碍物
}
printf("请输入起点坐标,以逗号分隔:\n");
scanf("%d,%d", &start_x,&start_y);
printf("请输入终点坐标,以逗号分隔:\n");
scanf("%d,%d", &end_x, &end_y);
visit[start_x][start_y] = 1;
dfs(start_x, start_y,0);
printf("最短步数为 %d 步.\n", min_step);
system("pause");
return 0;
}
测试数据如下:逗号为英文字符,可以在记事本中一起直接复制粘贴。
上述代码还有优化的地方。
//定义方向数组
int dx[4] = { 0,1,0,-1 };
int dy[4] = { 1,0,-1,0 };
void dfs(int x,int y,int step)
{
if (x== end_x && y== end_y)
{
//如果当前步数小于最小值,就更新
if (step < min_step)
min_step = step;
return;//回溯
}
//顺时针试探
for (int k = 0; k < 4; k++)
{
//tx,ty表示下一个方向的试探坐标
int tx, ty;
tx = x + dx[k];
ty = y + dy[k];
if (map[tx][ty]==1&& visit[tx][ty]==0)
{
visit[tx][ty] = 1;
dfs(tx, ty, step + 1);
visit[tx][ty] = 0;
}
}
return;
}
以下部分内容来自于B站视频 – 懒猫老师-数据结构-(8)栈的应用:迷宫问题(DFS)
视频链接:https://www.bilibili.com/video/BV1oE41177wk?spm_id_from=333.999.0.0.
M+2和N+2表示的是围墙的两行和两列。
这里走的迷宫遵循的都是顺时针原则:即 右-下-左-上 的原则。
最原汁原味版的迷宫程序
stack.h
#ifndef _STACK_H_
#define _STACK_H_
#include "maze.h"
const int MAX_SIZE = 100;//定义栈最大值常量
typedef void llist_op(struct Box recode);
template <class DataType>
class Stack
{
private:
struct Box* data;
int size;
int top;
public:
Stack();
Stack(int s);
~Stack();
void push(DataType ch);
DataType pop();
DataType getTop();
bool isEmpty();
bool isFull();
void setNull();
void travel(llist_op op);
class Full {};
class Empty {}; //由于这两个类都是放在Stack这个类声明的里面, 所以我们将其称为异常内部类
};
typedef Stack<struct Box> BoxStack;
#endif // !_STACK_H_
stack.cpp
#include "stack.h"
template <class DataType>
Stack<DataType>::Stack()
{
size = MAX_SIZE;
top = -1;
data = new DataType[MAX_SIZE];//缺省构造函数分配最大内存空间
}
template <class DataType>
Stack<DataType>::Stack(int s)
{
size = s;
top = -1;
data = new DataType[size];//根据指定的大小分配栈的内存空间
}
//析构函数
template <class DataType>
Stack<DataType>::~Stack()
{
delete[]data;//内存回收
}
template <class DataType>
void Stack<DataType>::push(DataType ch)
{
//当堆栈是满时,丢出异常对象
if (isFull())
throw Full();
data[++top] = ch;
}
template <class DataType>
DataType Stack<DataType>::getTop() //成员函数:获得栈顶元素(不出栈)
{
//当堆栈是空时,丢出异常对象
if (isEmpty())
throw Empty();
return data[top];
}
template <class DataType>
bool Stack<DataType>::isEmpty()
{
if (top == -1)
return true;
else
return false;
}
template <class DataType>
bool Stack<DataType>::isFull()
{
if (top+1 == size)
return true;
else
return false;
}
template <class DataType>
DataType Stack<DataType>::pop() //成员函数:出栈
{
if (isEmpty())
throw Empty();
return data[top--]; //先出栈,top再自减
}
template <class DataType>
void Stack<DataType>::setNull()
{
top = -1;
}
template <class DataType>
void Stack<DataType>::travel(llist_op op)
{
if (isEmpty())
throw Empty();
for (int i = 0; i <= top; i++)
{
op( data[i]);
}
}
template class Stack<Box>;
maze.h
#ifndef _MAZE_H_
#define _MAZE_H_
#include "stack.h"
//方向试探表示
typedef struct
{
//x,y方向的增量
int incX, incY;
}Direction;
//起点和终点坐标
typedef struct Setting
{
int start_x, start_y; //起点坐标:(start_x, start_y)
int end_x, end_y; //终点坐标:(endx, endy)
}Setting;
//栈中数据元素的组织
//栈中元素是一个由行、列、方向组成的三元组
typedef struct Box {
int x;
int y; //当前访问的迷宫格子的x,y坐标
int di; //当前方向
}Box;
#endif // !_MAZE_H_
maze.cpp
#include <iostream>
#include <iomanip>
#include "maze.h"
#include "stack.h"
using namespace std;
int maze[101][101];//0表示空地,1表示障碍物
bool findPath(int map[101][101], Direction direct[], BoxStack &s, Setting set)
{
Box temp;
int x, y, di; //迷宫格子当前处理单元的xy坐标和方向
int line, col; //迷宫数组下一个单元的行坐标和列坐标
map[set.start_x][set.start_y] = -1;
temp = { 1,1,-1 };
s.push(temp);
while (!s.isEmpty()) //栈不为空
{
temp = s.pop();
x = temp.x; y = temp.y; di = temp.di + 1;
while (di < 4) //方向未尝试完
{
line = x + direct[di].incX;
col = y + direct[di].incY;
if (map[line][col] == 0)
{
temp = { x,y,di };
s.push(temp);
x = line; y = col; map[line][col] = -1;
if (x == set.end_x && y == set.end_y)
return true;//迷宫有路
else di = 0;
}
else
di++;
}
}
return false;//迷宫没有路
}
void creatMaze(int m, int n)
{
int i, j = 0;
for (i = 0; j <= n + 1; j++)
maze[i][j] = 1; //0表示空地,1表示障碍物
for(i = m+1,j=0; j <= n + 1; j++)
maze[i][j] = 1; //0表示空地,1表示障碍物
for(i=0,j = 0; i <= m ; i++)
maze[i][j] = 1; //0表示空地,1表示障碍物
for (i=0,j = n+1; i <= m; i++)
maze[i][j] = 1; //0表示空地,1表示障碍物
}
void Print_Maze(int m, int n)
{
cout << "迷宫生成如下:";
for (int i = 0; i <= m + 1; i++)
{
cout << endl;
for (int j = 0; j <= n + 1; j++)
cout << setw(3)<<maze[i][j] ;
}
cout << endl;
}
//如果用户能给我提供这样一个数据,我就可以将它给输出出来.
void print_s(Box recode)
{
cout << recode.x <<" "<<recode.y<<" "<<recode.di<< endl;
}
int main(void)
{
int m, n;
Direction direct[4];
Setting set;
BoxStack s;
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;//上
cout<<"请设置迷宫的行数和列数,最大行数和列数均为100,输入以空格分隔。"<<endl;
while (1)
{
cin >> m >> n;
if (m > 100 || n > 100)
cout << "输入有误,请重新输入:" << endl;
else
break;
}
cout << "请输入" << m << "行" << n <<"列的迷宫,"<< "输入以空格分隔。"<<endl;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
cin >> maze[i][j]; //0表示空地,1表示障碍物
}
creatMaze(m, n);
Print_Maze(m, n);
cout << "请输入起点坐标,起点坐标需为空地且在地图上,输入以空格分隔。" << endl;
while (1)
{
cin >> set.start_x >> set.start_y;
if (set.start_x > m || set.start_y > n||maze[set.start_x][set.start_y]==1)
cout << "起点坐标设置有误,请重新输入:" << endl;
else
break;
}
cout << "请输入终点坐标,终点坐标不能与起点坐标相同、需为空地且在地图上,输入以空格分隔。" << endl;
while (1)
{
cin >> set.end_x >> set.end_y;
if (set.end_x > m || set.end_y > n || maze[set.end_x][set.end_y] == 1|| (set.end_x== set.start_x&& set.end_y == set.start_y))
cout << "终点坐标设置有误,请重新输入:" << endl;
else
break;
}
if (findPath(maze, direct, s, set))
{
cout << "寻路成功" << endl;
try
{
s.travel(print_s);
}
catch (Stack<struct Box>::Empty)
{
cout << "Stack Empty" << endl;
}
}
else
cout << "迷宫没有路" << endl;
Print_Maze(m, n);
system("pause");
return 0;
}