首先我们来看一下要如何使用堆栈去实现这个迷宫的求解
int mg[M + 2][N + 2] =
{ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
那有些小伙伴就很以后,这要怎么去回退呢?都已经走过了还怎么回去呀
了解了它的实现原理,接下来让我们一起来看一下怎么声明出它的结构
typedef struct {
int i; //当前方块的行号
int j; //当前方块的列号
int di; //下一相邻可走方位的方位号
}Box;
typedef struct {
Box data[MAX]; //数据
int top; //栈顶指针
}SqStack;
接下来就是正常的顺序栈的各种实现方式,用于我们将迷宫中的方块放入以及拿出堆栈
/*初始化栈*/
void InitStack(SqStack*& s)
{
s = (SqStack*)malloc(sizeof(SqStack));
s->top = -1; //初始化栈中无元素
}
/*摧毁栈*/
void DestroyStack(SqStack*& s)
{
free(s);
}
/*判断栈是否为空*/
bool StackEmpty(SqStack* s)
{
return s->top == -1; //等于-1则表示栈为空
}
/*入栈*/
bool Push(SqStack*& s, Box e)
{
if (s->top == MAX - 1) //说明当前栈已满
return false;
s->top++; //需要入栈,要先在栈中开辟一个顶端元素
s->data[s->top] = e; //将给到的e赋值进对应的数据仓
return true;
}
/*出栈*/
bool Pop(SqStack*& s, Box& e)
{
if (s->top == -1) //说明当前栈无元素可出栈
return false;
e = s->data[s->top];
s->top--;
return true;
}
/*获取栈顶元素*/
bool GetTop(SqStack* s, Box& e)
{
if (s->top == -1) //说明当前栈无元素可出栈
return false;
e = s->data[s->top];
//s->top--; 获取栈顶元素不会减少栈中的元素
return true;
}
/*显示栈*/
void DisplayStack(SqStack* s)
{
int i = 0;
while (i <= s->top)
{
printf("%d ", s->data[i++]);
}
printf("\n");
}
以上代码不做过多解释,接下来我们来构思一下大体要实现的代码框架体系
清楚了结构的声明以及代码逻辑的实现,接下去我们就要通过代码去实现找出这条实现路径
第一步
首先第一步是初始化以及将入口入栈
//1.初始化操作
bool MyPath(int xi, int yi, int xo, int yo)
{
Box path[MAX], e;
bool find;
int i, j, di,k;
SqStack* st; //存放迷宫方块的堆栈
InitStack(st);
e.i = xi;
e.j = yi; //设e为入口
e.di = -1; //初始化时出口e无下一个可走方块
Push(st, e);
mg[xi][yi] = -1; //将入口入栈后即表示此处不可再走
然后第二步就是要判断是否到达出口并且打印输出路径
while (!StackEmpty(st))
{
//2.若有一条可输出路径,则打印
//获取栈顶元素
GetTop(st, e);
i = e.i; j = e.j; di = e.di;
//若取出的栈顶元素为迷宫的一条路径,则进行输出
if (i == xo && j == yo)
{
puts("找到一条迷宫路径如下");
k = 0;
while (!StackEmpty(st))
{
Pop(st, e); //出栈栈顶元素
path[k++] = e; //将从栈顶取出的迷宫方块依此放入path路径中
}
//逆序,输出路径
while (k >= 1)
{
k--;
printf("\t(%d,%d)", path[k].i, path[k].j);
if ((k + 2) % 5 == 0)
printf("\n");
}
printf("\n");
DestroyStack(st);
return true;
}
接着第三步就是判断若是没有找到所需要的出口,那如何往四个方位去遍历
//3.若还未找到可走方块,则继续往四周寻找
int i1 = 0, j1 = 0; //用于存放下一个可走方块的位置
find = false;
while (di < 4 && !find)
{
di++;
switch (di)
{
case 0:i1 = i - 1; j1 = j; break;
case 1:i1 = i; j1 = j + 1; break;
case 2:i1 = i + 1; j1 = j; break;
case 3:i1 = i; j1 = j - 1; break;
}
if (mg[i1][j1] == 0) find = true;
}
最后是第四步去判断是否找到了一个可走方块,然后对应地进行操作
//4.判断栈中方块是否可走
if (find) { //若当前方块可走
st->data[st->top].di = di;
e.i = i1; e.j = j1; e.di = -1;
Push(st, e);
mg[i1][j1] = -1;
}
else { //若当前方块不可走
Pop(st, e); //将当前方块出栈
mg[e.i][e.j] = 0; //将当前方块的位置复原,供其他路径使用
}
了解了整体的代码逻辑,接下去我们将这个代码进行一个结果的测试
从结果看来我们是成功了,打印除了一条从入口到出口,从(1,1)到(8,8)的路径,每行输出5个,大家也可以去自己的编译器上运行一下看看
下面是整体的代码展示,供需要的小伙伴测试
#pragma once
#include
#include
#define M 8
#define N 8
#define MAX 100
typedef struct {
int i; //当前方块的行号
int j; //当前方块的列号
int di; //下一相邻可走方位的方位号
}Box;
typedef struct {
Box data[MAX]; //数据
int top; //栈顶指针
}SqStack;
/*初始化栈*/
void InitStack(SqStack*& s)
{
s = (SqStack*)malloc(sizeof(SqStack));
s->top = -1; //初始化栈中无元素
}
/*摧毁栈*/
void DestroyStack(SqStack*& s)
{
free(s);
}
/*判断栈是否为空*/
bool StackEmpty(SqStack* s)
{
return s->top == -1; //等于-1则表示栈为空
}
/*入栈*/
bool Push(SqStack*& s, Box e)
{
if (s->top == MAX - 1) //说明当前栈已满
return false;
s->top++; //需要入栈,要先在栈中开辟一个顶端元素
s->data[s->top] = e; //将给到的e赋值进对应的数据仓
return true;
}
/*出栈*/
bool Pop(SqStack*& s, Box& e)
{
if (s->top == -1) //说明当前栈无元素可出栈
return false;
e = s->data[s->top];
s->top--;
return true;
}
/*获取栈顶元素*/
bool GetTop(SqStack* s, Box& e)
{
if (s->top == -1) //说明当前栈无元素可出栈
return false;
e = s->data[s->top];
//s->top--; 获取栈顶元素不会减少栈中的元素
return true;
}
/*显示栈*/
void DisplayStack(SqStack* s)
{
int i = 0;
while (i <= s->top)
{
printf("%d ", s->data[i++]);
}
printf("\n");
}
#include
#include "stack.hpp"
//自己绘制一条迷宫路径
int mg[M + 2][N + 2] =
{ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
//求解路径
bool MyPath(int xi, int yi, int xo, int yo)
{
//1.初始化操作
Box path[MAX], e;
bool find;
int i, j, di,k;
SqStack* st; //存放迷宫方块的堆栈
InitStack(st);
e.i = xi;
e.j = yi; //设e为入口
e.di = -1; //初始化时出口e无下一个可走方块
Push(st, e);
mg[xi][yi] = -1; //将入口入栈后即表示此处不可再走
while (!StackEmpty(st))
{
//2.若有一条可输出路径,则打印
//获取栈顶元素
GetTop(st, e);
i = e.i; j = e.j; di = e.di;
//若取出的栈顶元素为迷宫的一条路径,则进行输出
if (i == xo && j == yo)
{
puts("找到一条迷宫路径如下");
k = 0;
while (!StackEmpty(st))
{
Pop(st, e); //出栈栈顶元素
path[k++] = e; //将从栈顶取出的迷宫方块依此放入path路径中
}
//逆序输出路径
while (k >= 1)
{
k--;
printf("\t(%d,%d)", path[k].i, path[k].j);
if ((k + 2) % 5 == 0)
printf("\n");
}
printf("\n");
DestroyStack(st);
return true;
}
//3.若还未找到可走方块,则继续往四周寻找
int i1 = 0, j1 = 0; //用于存放下一个可走方块的位置
find = false;
while (di < 4 && !find)
{
di++;
switch (di)
{
case 0:i1 = i - 1; j1 = j; break;
case 1:i1 = i; j1 = j + 1; break;
case 2:i1 = i + 1; j1 = j; break;
case 3:i1 = i; j1 = j - 1; break;
}
if (mg[i1][j1] == 0) find = true;
}
//4.判断栈中方块是否可走
if (find) { //若当前方块可走
st->data[st->top].di = di;
e.i = i1; e.j = j1; e.di = -1;
Push(st, e);
mg[i1][j1] = -1;
}
else { //若当前方块不可走
Pop(st, e); //将当前方块出栈
mg[e.i][e.j] = 0; //将当前方块的位置复原,供其他路径使用
}
}
//若栈出空了还是没有找到一条合适的路径,则返回false
DestroyStack(st);
return false;
}
int main(void)
{
if (!MyPath(1, 1, M, N))
{
puts("该迷宫问题没有解");
}
return 1;
}
对于队列,我们都不陌生,它是一个先进先出【FIFO】的数据结构
明白了队列在迷宫中如何去寻找一条最短路径的底层原理,接下来我们就要往代码层面去分析,首先就是将封装的结构体声明出来
typedef struct
{
int i, j; //方块的位置
int pre; //本路径中上一个方块在队列中的下标
}Box; //方块类型
typedef struct Queue {
Box data[MAX];
int front, rear; //队头、队尾指针
}QuType; //顺序队
了解了队列结构该如何去设计,接下来我们要去构思整个程序该如何去实现
了解了队列的寻找路径的底层实现,对总体的设计框架也有了一个很清晰的认识,接下去我们就需要专注于去实现这些代码
int mg[M + 2][N + 2] =
{ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
bool mgpath(int xi, int yi, int xo, int yo)
{ //入口(xi,yi) 出口(xo,yo)
Box e; //定义一个方块用以存储走过的位置 - 需要操作的方块
int i, j, i1, j1, di; //i,j表示找到的结果方块的位置
//i1,j1表示还需遍历的四周其他方块的位置
//初始化队列
QuType* q;
InitQueue(q);
//初始化入口方块
e.i = xi;
e.j = yi;
e.pre = -1;
EnQueue(q, e); //入队
mg[e.i][e.j] = -1; //入口的索引值设置为-1
while (!QueueEmpty(q))
{
//若找到迷宫的一条路径
DeQueue(q, e); //将当前队头的方块e出队,判断是否为出口
i = e.i;
j = e.j;
if (i == xo && j == yo) //若不为出口,不进判断,不打印
{
print(q, q->front); //反推出一条对应的迷宫路径并输出
DestroyQueue(q);
return true;
//找到一条路径,则销毁队列,返回true
}
void print(QuType* qu, int front)
{
int k = front, j, ns = 0;
cout << endl;
do
{ //反向找到最短路径
j = k;
k = qu->data[k].pre;
qu->data[j].pre = -1; //将该路径上方块的pre成员设置为-1
} while (k != 0);
cout << "有一条迷宫路径如下:" << endl;
k = 0;
while (k < MAX)
{
if (qu->data[k].pre == -1)
{ //正向搜索到pre为-1的方块,即构成正向的路径
ns++;
cout << "\t(" << qu->data[k].i <<"," << qu->data[k].j <<")";
if (ns % 5 == 0) //每输出五个方块换行
cout << endl;
}
k++;
}
cout << endl;
}
//若找到的不是出口,则继续遍历相邻的其他方块
for (di = 0; di < 4; ++di)
{
switch (di)
{
case 0: i1 = i - 1; j1 = j; break;
case 1: i1 = i; j1 = j + 1; break;
case 2: i1 = i + 1; j1 = j; break;
case 3: i1 = i; j1 = j - 1; break;
}
if (mg[i1][j1] == 0) //如果发现方块是可以走的
{
e.i = i1;
e.j = j1; //把当前方块的坐标给到e
e.pre = q->front; //将此前方块与前一个方块相连
EnQueue(q, e);
mg[i1][j1] = -1;
}
}
我们先展示一下整体的代码,有些小伙伴等不及了,下一版块再去对比分析
#pragma once
#pragma once
#include
#include
#define MAX 50
#define M 8
#define N 8
typedef struct
{
int i, j; //方块的位置
int pre; //本路径中上一个方块在队列中的下标
}Box; //方块类型
typedef struct Queue {
Box data[MAX];
int front, rear; //队头、队尾指针
}QuType; //顺序队
/*初始化队列*/
void InitQueue(QuType*& q)
{
q = (QuType*)malloc(sizeof(QuType));
q->front = q->rear = -1;
}
/*摧毁队列*/
void DestroyQueue(QuType*& q)
{
free(q);
}
/*判断队列是否为空*/
bool QueueEmpty(QuType* q)
{
return q->front == q->rear;
}
/*入队*/
bool EnQueue(QuType*& q, Box e)
{
if (q->rear == MAX - 1)
return false;
else
{
q->rear++;
q->data[q->rear] = e;
return true;
}
}
/*出队*/
bool DeQueue(QuType*& q, Box& e)
{
if (q->front == q->rear) //表示队列为空
return false;
else
{
q->front++;
e = q->data[q->front];
return true;
}
}
/*求队列长度*/
int QuLength(QuType* q)
{
return ((q->rear - q->front) + MAX) % MAX;
}
/*显示队列*/
void DisplayQueue(QuType* q)
{
int len = QuLength(q);
Box e;
for (int i = 0; i < len; ++i)
{
DeQueue(q, e);
printf("%d ", e);
}
printf("\n");
}
#include
#include "listQ.hpp"
#include
using namespace std;
//需要利用已经出队的元素
int mg[M + 2][N + 2] =
{ {1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,1,0,0,0,1,0,1},
{1,0,0,0,0,1,1,0,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
void print(QuType* qu, int front)
{
int k = front, j, ns = 0;
cout << endl;
do
{ //反向找到最短路径
j = k;
k = qu->data[k].pre;
qu->data[j].pre = -1; //将该路径上方块的pre成员设置为-1
} while (k != 0);
cout << "有一条迷宫路径如下:" << endl;
k = 0;
while (k < MAX)
{
if (qu->data[k].pre == -1)
{ //正向搜索到pre为-1的方块,即构成正向的路径
ns++;
cout << "\t(" << qu->data[k].i <<"," << qu->data[k].j <<")";
if (ns % 5 == 0) //每输出五个方块换行
cout << endl;
}
k++;
}
cout << endl;
}
bool mgpath(int xi, int yi, int xo, int yo)
{ //入口(xi,yi) 出口(xo,yo)
Box e; //定义一个方块用以存储走过的位置 - 需要操作的方块
int i, j, i1, j1, di; //i,j表示找到的结果方块的位置
//i1,j1表示还需遍历的四周其他方块的位置
//初始化队列
QuType* q;
InitQueue(q);
//初始化入口方块
e.i = xi;
e.j = yi;
e.pre = -1;
EnQueue(q, e); //入队
mg[e.i][e.j] = -1; //入口的索引值设置为-1
while (!QueueEmpty(q))
{
//若找到迷宫的一条路径
DeQueue(q, e); //将当前队头的方块e出队,判断是否为出口
i = e.i;
j = e.j;
if (i == xo && j == yo) //若不为出口,不进判断,不打印
{
print(q, q->front); //反推出一条对应的迷宫路径并输出
DestroyQueue(q);
return true;
//找到一条路径,则销毁队列,返回true
}
//若找到的不是出口,则继续遍历相邻的其他方块
for (di = 0; di < 4; ++di)
{
switch (di)
{
case 0: i1 = i - 1; j1 = j; break;
case 1: i1 = i; j1 = j + 1; break;
case 2: i1 = i + 1; j1 = j; break;
case 3: i1 = i; j1 = j - 1; break;
}
if (mg[i1][j1] == 0) //如果发现方块是可以走的
{
e.i = i1;
e.j = j1; //把当前方块的坐标给到e
e.pre = q->front; //将此前方块与前一个方块相连
EnQueue(q, e);
mg[i1][j1] = -1;
}
}
}
DestroyQueue(q);
return false; //若遍历结束还是没找到一条路径,则销毁队列,返回false
}
int main(void)
{
if (!mgpath(1, 1, M, N))
cout << "该迷宫问题没有解" << endl;
return 1;
}
堆栈寻找的路径
队列寻找的路径
其实我们的人生何尝不是在走迷宫,一直在寻找一条自己认为对的出路,但又时常迷失方向,原因就是大家都想要走捷径,都希望通过任何方式去寻找一条通往成功的最短路径,最终虽赢得了世界,但却失去了自己原本最宝贵的东西——❤初心❤
本文我们重点讲解了如何通过堆栈和队列去查找一条迷宫的路径问题,也分析两种数据结构的底层是原理,分析了他们在实现一条路径的时候各有优劣,视具体场景而定
以上就是本文的所有内容,如有疑问请于评论区留言或者私信我,感谢您的观看