目录
5.10判断当前位置是否可通(通过判断下一个位置时墙、死路、还是访问过)
使用栈实现迷宫路径搜索算法
在编程中,迷宫问题是一个经典的搜索问题,通常用于演示图搜索算法,如深度优先搜索(DFS)或广度优先搜索(BFS)。然而,这里我们将使用栈来实现一个深度优先搜索的迷宫路径搜索算法。栈是一种后进先出(LIFO)的数据结构,非常适合用于递归或迭代实现的深度优先搜索。
迷宫表示
迷宫通常用一个二维数组表示,其中0表示不可通行的墙壁,1表示可以通行的路径。为了记录搜索过程,我们还需要两个额外的状态:2表示已经访问过的路径,-1表示尝试但无法通行的路径。
算法思路
- 初始化:设置起点和终点,初始化栈。
- 搜索:从起点开始,尝试四个方向(东、南、西、北)的移动。
- 判断:如果当前位置是终点,则找到路径;如果当前位置可通行且未访问过,则标记为已访问并继续搜索;如果当前位置不可通行或已访问过,则回溯到上一个位置。
- 回溯:如果当前位置的所有方向都尝试过且无法继续,则从栈中弹出上一个位置,继续尝试其他方向。
- 结束:当栈为空时,表示所有可能的路径都已尝试,无法找到路径。
代码实现
以下是使用C语言实现的迷宫路径搜索算法的代码。
1.全部代码
#include<stdio.h>
#include<stdlib.h>
#define MAZESIZE 10
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 2
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;//状态,函数返回值
typedef int Bool;
typedef struct
{
int x, y;
}PosType;//坐标
typedef struct
{
int ord;//当前位置在当前路径的序号
PosType seat;
int di;//方向
}SElemType;//栈元素类型
typedef struct
{
SElemType* top, * base;
int stacksize;
}SqStack;//栈结构
typedef struct
{
int scene[MAZESIZE][MAZESIZE];
PosType start, end;
}MazeType;//迷宫类型
//以下是函数声明
Status MazePath(MazeType, SqStack*);
Status InitStack(SqStack*);
int Pass(MazeType, PosType);//留下访问过的足迹
void SetCurSElem(int, PosType, int, SElemType*);
Status Push(SqStack*, SElemType);
void FootPrint(MazeType*, PosType);
Bool isReach(MazeType, PosType);
PosType NextPos(PosType, int);
Bool StackEmpty(SqStack);
void MarkPrint(MazeType*, PosType);//标记死路
Status Pop(SqStack*, SElemType*);
Bool isAccess(MazeType, PosType,int);
Status StackTraverse(SqStack);
void visit(SElemType);
int main()
{
MazeType maze =
{
{ {0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,1,1,0,0,1,1,0},
{0,1,0,0,0,1,1,1,1,0},
{0,1,1,1,0,1,1,1,1,0},
{0,1,0,1,1,1,0,1,1,0},
{0,1,0,0,0,1,0,0,1,0},
{0,0,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0}
},
{1,1},
{8,8},
};//初始化迷宫,生成迷宫场景
SqStack S;
InitStack(&S);//构造空栈
if (MazePath(maze, &S))//调用迷宫路径搜索函数
{
printf("The path is:\n");
StackTraverse(S);
}
else
{
printf("No path found.");
}
return 0;
}
//迷宫通路搜索函数
Status MazePath(MazeType maze, SqStack*pS)
{//若迷宫maze存在从入口start到出口end通道,则求得一条存放在栈中(从栈底
//到栈顶,这条路径的特点是所有方向的和一定是最小的,仅对该算法而言),
//并返回TRUE;否则返回FALSE
PosType CurPos = maze.start;//设定“当前位置”为“入口位置”
int CurStep = 1;
SElemType e;
int i;
do
{//退出while循环的两种可能,找到出口或迷宫无解(栈空,由于栈的先进后出的特点,
//它一定是以回到起点尝试了最后方向,发现无路可走而结束的)
i = Pass(maze, CurPos);//判断当前位置是否可通,返回最小可通方向
if (i)//可能值1,2,3,4
{
SetCurSElem(CurStep, CurPos, i, &e);
Push(pS, e);//加入路径,准备走向可通方向上的下一个位置
FootPrint(&maze, CurPos);//留下足迹,不影响退栈
if (isReach(maze, CurPos))return TRUE;//到达出口
CurPos = NextPos(CurPos, i);//走向下一个位置
CurStep++;//准备下一步的探索
}
else//当前位置不可通
{
MarkPrint(&maze, CurPos);//标记死路
if (!StackEmpty(*pS))//栈非空,可以回到上一个位置
{
Pop(pS, &e);//准备回到上一个位置
CurPos = e.seat;//回到上一个位置,进行新一轮的判断
CurStep = e.ord;
}
}
} while (!StackEmpty(*pS));
return FALSE;
}
//1.构造一个空栈(栈的基本操作)
Status InitStack(SqStack* pS)
{
pS->base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!pS->base)return ERROR;
pS->top = pS->base;
pS->stacksize = STACK_INIT_SIZE;
return OK;
}
//2.判断当前位置是否可通,并返回可通的最小方向,否则返回0
//通过判断下一个位置在迷宫中的性质(通路,墙,死路,访问过)
int Pass(MazeType maze, PosType pos)
{
int i;
for(i=1;i<=4;i++)
if (isAccess(maze, pos,i))
{
return i;
}
return 0;
}
//3.将待入栈的元素设置为特定值
void SetCurSElem(int ord, PosType pos, int di, SElemType* p)
{
p->ord = ord;
p->seat = pos;//同一结构体类型可以赋值
p->di = di;
}
//4.插入元素e为新的栈顶元素(栈的基本操作)
Status Push(SqStack* pS, SElemType e)
{
if (pS->top - pS->base >= pS->stacksize)//栈满,追加空间
{
pS->base = (SElemType*)realloc(pS->base,
(pS->stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!pS->base)return ERROR;
pS->top = pS->base + pS->stacksize;
pS->stacksize += STACKINCREMENT;
}
*pS->top++ = e;
return OK;
}
//5.在迷宫相应位置标记2,代表访问过
void FootPrint(MazeType* pMaze, PosType pos)
{
pMaze->scene[pos.x][pos.y] = 2;
}
//6.判断是否到达出口(栈顶元素)
Bool isReach(MazeType maze, PosType pos)
{
return pos.x == maze.end.x && pos.y == maze.end.y;
}
//7.返回下一个位置(坐标)
PosType NextPos(PosType pos, int i)
{
PosType next = pos;
switch (i)
{
case 1:next.y = pos.y + 1; break;
case 2:next.x = pos.x + 1; break;
case 3:next.y = pos.y - 1; break;
case 4:next.x = pos.x - 1;
}
return next;
}
//8.标记死路,将迷宫相应位置改为-1
void MarkPrint(MazeType* pMaze, PosType pos)
{
pMaze->scene[pos.x][pos.y] = -1;
}
//9.判断是否栈空
Bool StackEmpty(SqStack S)
{
return S.top == S.base;
}
//10.弹出一个栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack* pS, SElemType* p)
{
if (!StackEmpty(*pS))
{
*p = *--pS->top;
return OK;
}
return ERROR;
}
//11.判断当前位置的下一个位置是否可通
Bool isAccess(MazeType maze, PosType pos,int i)
{
PosType next = NextPos(pos, i);
return maze.scene[next.x][next.y] == 1;
}
//12.遍历栈,打印路径
Status StackTraverse(SqStack S)
{
SElemType* p;
p = S.base;
while (p)
{
visit(*p++);
}
return OK;
}
//13.打印路径信息
void visit(SElemType e)
{
printf("Step %d:(%d,%d),next:%d\n", e.ord, e.seat.x, e.seat.y, e.di);
}
2.头文件和宏定义
#include<stdio.h>
#include<stdlib.h>
#define MAZESIZE 10
#define STACK_INIT_SIZE 20
#define STACKINCREMENT 2
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
typedef int Status;//状态,函数返回值
typedef int Bool;
typedef struct
{
int x, y;
}PosType;//坐标
typedef struct
{
int ord;//当前位置在当前路径的序号
PosType seat;
int di;//方向
}SElemType;//栈元素类型
typedef struct
{
SElemType* top, * base;
int stacksize;
}SqStack;//栈结构
typedef struct
{
int scene[MAZESIZE][MAZESIZE];
PosType start, end;
}MazeType;//迷宫类型
//以下是函数声明
Status MazePath(MazeType, SqStack*);
Status InitStack(SqStack*);
int Pass(MazeType, PosType);//留下访问过的足迹
void SetCurSElem(int, PosType, int, SElemType*);
Status Push(SqStack*, SElemType);
void FootPrint(MazeType*, PosType);
Bool isReach(MazeType, PosType);
PosType NextPos(PosType, int);
Bool StackEmpty(SqStack);
void MarkPrint(MazeType*, PosType);//标记死路
Status Pop(SqStack*, SElemType*);
Bool isAccess(MazeType, PosType,int);
Status StackTraverse(SqStack);
void visit(SElemType);
3.主调函数
int main()
{
MazeType maze =
{
{ {0,0,0,0,0,0,0,0,0,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,0,1,1,1,0,1,0},
{0,1,1,1,1,0,0,1,1,0},
{0,1,0,0,0,1,1,1,1,0},
{0,1,1,1,0,1,1,1,1,0},
{0,1,0,1,1,1,0,1,1,0},
{0,1,0,0,0,1,0,0,1,0},
{0,0,1,1,1,1,1,1,1,0},
{0,0,0,0,0,0,0,0,0,0}
},
{1,1},
{8,8},
};//初始化迷宫,生成迷宫场景
SqStack S;
InitStack(&S);//构造空栈
if (MazePath(maze, &S))//调用迷宫路径搜索函数
{
printf("The path is:\n");
StackTraverse(S);
}
else
{
printf("No path found.");
}
return 0;
}
4.核心函数
//迷宫通路搜索函数
Status MazePath(MazeType maze, SqStack*pS)
{//若迷宫maze存在从入口start到出口end通道,则求得一条存放在栈中(从栈底
//到栈顶,这条路径的特点是所有方向的和一定是最小的,仅对该算法而言),
//并返回TRUE;否则返回FALSE
PosType CurPos = maze.start;//设定“当前位置”为“入口位置”
int CurStep = 1;
SElemType e;
int i;
do
{//退出while循环的两种可能,找到出口或迷宫无解(栈空,由于栈的先进后出的特点,
//它一定是以回到起点尝试了最后方向,发现无路可走而结束的)
i = Pass(maze, CurPos);//判断当前位置是否可通,返回最小可通方向
if (i)//可能值1,2,3,4
{
SetCurSElem(CurStep, CurPos, i, &e);
Push(pS, e);//加入路径,准备走向可通方向上的下一个位置
FootPrint(&maze, CurPos);//留下足迹,不影响退栈
if (isReach(maze, CurPos))return TRUE;//到达出口
CurPos = NextPos(CurPos, i);//走向下一个位置
CurStep++;//准备下一步的探索
}
else//当前位置不可通
{
MarkPrint(&maze, CurPos);//标记死路
if (!StackEmpty(*pS))//栈非空,可以回到上一个位置
{
Pop(pS, &e);//准备回到上一个位置
CurPos = e.seat;//回到上一个位置,进行新一轮的判断
CurStep = e.ord;
}
}
} while (!StackEmpty(*pS));
return FALSE;
}
5.辅助函数
5.1构造一个空栈
//1.构造一个空栈(栈的基本操作)
Status InitStack(SqStack* pS)
{
pS->base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!pS->base)return ERROR;
pS->top = pS->base;
pS->stacksize = STACK_INIT_SIZE;
return OK;
}
5.2判断是否可通,并返回可通的方向
//2.判断当前位置是否可通,并返回可通的最小方向,否则返回0
//通过判断下一个位置在迷宫中的性质(通路,墙,死路,访问过)
int Pass(MazeType maze, PosType pos)
{
int i;
for(i=1;i<=4;i++)
if (isAccess(maze, pos,i))
{
return i;
}
return 0;
}
5.3将待入栈的元素设置指定值
//3.将待入栈的元素设置为特定值
void SetCurSElem(int ord, PosType pos, int di, SElemType* p)
{
p->ord = ord;
p->seat = pos;//同一结构体类型可以赋值
p->di = di;
}
5.4插入元素e为新的栈顶元素
//4.插入元素e为新的栈顶元素(栈的基本操作)
Status Push(SqStack* pS, SElemType e)
{
if (pS->top - pS->base >= pS->stacksize)//栈满,追加空间
{
pS->base = (SElemType*)realloc(pS->base,
(pS->stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!pS->base)return ERROR;
pS->top = pS->base + pS->stacksize;
pS->stacksize += STACKINCREMENT;
}
*pS->top++ = e;
return OK;
}
5.4留下足迹,表示在当前路径中,并设置迷宫的相应位置为2
//5.在迷宫相应位置标记2,代表访问过
void FootPrint(MazeType* pMaze, PosType pos)
{
pMaze->scene[pos.x][pos.y] = 2;
}
5.5判断是否到达出口
//6.判断是否到达出口(栈顶元素)
Bool isReach(MazeType maze, PosType pos)
{
return pos.x == maze.end.x && pos.y == maze.end.y;
}
5.6返回下一个位置(坐标)
//7.返回下一个位置(坐标)
PosType NextPos(PosType pos, int i)
{
PosType next = pos;
switch (i)
{
case 1:next.y = pos.y + 1; break;
case 2:next.x = pos.x + 1; break;
case 3:next.y = pos.y - 1; break;
case 4:next.x = pos.x - 1;
}
return next;
}
5.7死路,标记不可通,设置迷宫的相应位置为-1
//8.标记死路,将迷宫相应位置改为-1
void MarkPrint(MazeType* pMaze, PosType pos)
{
pMaze->scene[pos.x][pos.y] = -1;
}
5.8判断是否为空栈
//9.判断是否栈空
Bool StackEmpty(SqStack S)
{
return S.top == S.base;
}
5.9出栈,弹出一个栈顶元素
//10.弹出一个栈顶元素,用e返回其值,并返回OK;否则返回ERROR
Status Pop(SqStack* pS, SElemType* p)
{
if (!StackEmpty(*pS))
{
*p = *--pS->top;
return OK;
}
return ERROR;
}
5.10判断当前位置是否可通(通过判断下一个位置时墙、死路、还是访问过)
//11.判断当前位置的下一个位置是否可通
Bool isAccess(MazeType maze, PosType pos,int i)
{
PosType next = NextPos(pos, i);
return maze.scene[next.x][next.y] == 1;
}
5.11遍历栈,打印路径
//12.遍历栈,打印路径
Status StackTraverse(SqStack S)
{
SElemType* p;
p = S.base;
while (p)
{
visit(*p++);
}
return OK;
}
5.12打印路径信息
//13.打印路径信息
void visit(SElemType e)
{
printf("Step %d:(%d,%d),next:%d\n", e.ord, e.seat.x, e.seat.y, e.di);
}
测试结果
运行上述代码,如果迷宫中存在从起点到终点的路径,程序将打印出该路径的每一步。每一步包括步数、当前位置坐标以及下一步的方向(1为东,2为南,3为西,4为北)。如果迷宫中不存在路径,则打印“No path found.”。
总结
通过栈实现的迷宫路径搜索算法是一种深度优先搜索的变体。它利用栈来保存搜索过程中的状态,并在无法继续搜索时回溯到上一个状态。这种算法在解决迷宫问题时非常有效,并且可以通过调整搜索策略来优化性能。
说明
这个是《数据结构(C语言版)》严蔚敏里的算法,C代码实现。学习记录p47