您的位置:首页 > 理论基础 > 数据结构算法

《数据结构》(C语言版)——栈的应用举例-迷宫求解

2018-03-07 22:14 447 查看

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
// 用到的库文件
#include <stdio.h> // printf();scanf()
#include <stdlib.h> // exit()
#include <malloc.h> // malloc()
#include <time.h> // srand((unsigned)time(NULL));
// 函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
// Status是函数的类型,其值是函数结果状态代码
typedef int Status;

// 3.2.4 迷宫求解----------------------------------------
// 迷宫地图数组,0墙,1通道, 8入口, 9出口
int mazeArr[10][10] = {
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 8, 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, 9, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};

// 通道块在迷宫中的“坐标位置”
typedef struct{
int x;
int y;
}PosType;

// 栈的元素类型
typedef struct{
int ord; // 通道块在路径上的“序号” order
PosType seat; // 通道块在迷宫中的“坐标位置” seat
int di; // 从此通道块走向下一通道块的“方向” direction 1:东 2:南 3:西 4:北
}SElemType;

// 迷宫
typedef struct {
int row, col; // 迷宫的行数,列数
int arr[10][10]; // 迷宫地图可标记数组
}MazeType;

// -----栈的链式存储结构----------------------------------
typedef struct SNode {
SElemType data; // 数据域
struct SNode *next; // 指针域
} SNode, *LinkStack;

Status visit(SElemType e);

// 操作结果:构造一个空栈S。
Status InitStack(LinkStack &S) {
S = (LinkStack)malloc(sizeof(SNode));
if(!S) // 存储分配失败
exit(OVERFLOW); // exit(-2)程序异常退出
S->next = NULL;
return OK;
}// InitStack

// 操作结果:销毁栈S,S不再存在。
Status DestroyStack(LinkStack &S) {
LinkStack p = S->next, ptmp; // p指向栈顶
while(p) { // p指向栈底时,循环停止
ptmp = p->next;
free(p); // 释放每个数据结点的指针域
p = ptmp;
}
free(S);
return OK;
}// DestroyStack

// 操作结果:把S置为空栈。
Status ClearStack(LinkStack &S) {
LinkStack p = S->next, ptmp; // p指向栈顶
while(p) { // p指向栈底时,循环停止
ptmp = p->next;
free(p); // 释放每个数据结点的指针域
p = ptmp;
}
S->next = NULL;
return OK;
}// ClearStack

// 操作结果:若S为空栈,返回TRUE,否则返回FALSE
Status StackEmpty(LinkStack S) {
if(S->next == NULL)
return TRUE;
b878
// 返回1
else
return FALSE; // 返回0
}// StackEmpty

// 操作结果:返回S的元素个数,即栈的长度。
int StackLength(LinkStack S) {
int n = 0;
LinkStack p = S->next; // p指向栈顶
while(p) {
n++;
p = p->next;
}
return n;
}// StackLength

// 操作结果:若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR。
Status GetTop(LinkStack S, SElemType &e) {
if ( S->next == NULL )
return ERROR; // 栈空
e = S->next->data; // 取栈顶元素
// printf("获取的栈顶元素:"); visit(e);
return OK;
}// GetTop

// 操作结果:插入元素e为新的栈顶元素。
Status Push(LinkStack &S, SElemType e) {
LinkStack p = (LinkStack)malloc(sizeof(SNode));
p->data = e;
p->next = S->next; // 新结点指向栈顶
S->next = p; // 更新栈顶指针
// printf("插入的栈顶元素:"); visit(e);
return OK;
}// Push

// 操作结果:若栈不空,则删除S的栈顶元素,并用e返回其值;否则返回ERROR。
Status Pop(LinkStack &S, SElemType &e) {
// 若1个元素也没有:
if (S->next == NULL)
return ERROR;
// 若有1个以上元素
e = S->next->data;
LinkStack ptmp = S->next->next;
free(S->next);
S->next = ptmp;
// printf("删除的栈顶元素:"); visit(e);
return OK;
}// Pop

Status visit(SElemType e) {
printf(" 序号:%2d, 坐标:(%d,%d), 方向:", e.ord, e.seat.x, e.seat.y);
switch(e.di)
{
case 1: printf("东"); break; // 东
case 2: printf("南"); break; // 南
case 3: printf("西"); break; // 西
case 4: printf("北"); break; // 北
}
printf("\n");
return OK;
}
// 操作结果:从 栈底到栈顶 依次对栈中每个数据元素调用函数visit()。一旦vistit()失败,刚操作失败。
Status StackTraverse(LinkStack S, Status (*pfn_visit)(SElemType)) {
if(S->next == NULL)
{
printf("栈为空!\n");
return ERROR;
}
for(int i=StackLength(S); i>0; i--)
{
LinkStack p = S->next; // p指向栈顶
int j = 1; // j为计数器
while ( p && j<i ) { // 顺指针向后查找,直到p指向第i个元素或p为空
p = p->next;
++j;
}
visit(p->data);
}
printf("\n");
return OK;
}// StackTraverse_base_to_Top
// 操作结果:从 栈顶到栈底 依次对栈中每个数据元素调用函数visit()。一旦vistit()失败,刚操作失败。
Status StackTraverse_Top(LinkStack S, Status (*pfn_visit)(SElemType)) {
if(S->next == NULL)
{
printf("栈为空!\n");
return ERROR;
}
LinkStack p = S->next; // p指向栈顶
while(p) {
visit(p->data);
p = p->next;
}
printf("\n");
return OK;
}// StackTraverse_Top_to_base

// 3.2.4 迷宫求解----------------------------------------
// 初始化迷宫
Status InitMaze(MazeType &maze)
{ // 复制迷宫地图数据
for(int i=0; i<10; i++)
for(int j=0; j<10; j++)
maze.arr[i][j] = mazeArr[i][j];
maze.row = 10;
maze.col = 10;
return OK;
}
// 判断当前位置是否可以通过
Status Pass(MazeType maze, PosType curpos)
{
if( maze.arr[curpos.x][curpos.y] == 1 || // 1代表通道
maze.arr[curpos.x][curpos.y] == 8 ||
maze.arr[curpos.x][curpos.y] == 9)
return TRUE;
else return FALSE;
}
// 留下足迹
Status FootPrint(MazeType &maze,PosType curpos)
{
maze.arr[curpos.x][curpos.y] = 2; // 2留下足迹
return OK;
}
// 标记不能通过
Status MarkPrint(MazeType &maze,PosType curpos)
{
maze.arr[curpos.x][curpos.y] = 3; // 3标记不能通过
return OK;
}
// 探索记录
SElemType CreatSElem(int curstep, PosType curpos, int di)
{
SElemType e;
e.ord = curstep; // 通道块在路径上的“序号”
e.seat = curpos; // 通道块在迷宫中的“坐标位置”
e.di = di; // 从此通道块走向下一通道块的“方向” 1:东 2:南 3:西 4:北
return e;
}

// 下一位置
PosType NextPos(PosType curpos, int di)
{
PosType pos = curpos;
switch(di)
{
case 1: pos.y++; break; // 东
case 2: pos.x++; break; // 南
case 3: pos.y--; break; // 西
case 4: pos.x--; break; // 北
}
return pos;
}
// 是否到达终点(出口)
Status PosEquare(PosType curpos, PosType end)
{
if(curpos.x == end.x && curpos.y == end.y)
return TRUE;
else return FALSE;
}
// 输出迷宫地图
void PrintMaze(MazeType maze)
{
// 输出x坐标
printf(" ");
for(int i=0; i<10; i++)
printf("%2d", i);
printf("\n");
// 输出y坐标,以及迷宫状态
for(i=0; i<10; i++)
{
printf("%2d", i);
for(int j=0; j<10; j++)
{
switch(maze.arr[i][j])
{
case 0: printf("■"); break; // 0墙
case 1: printf(" "); break; // 1通道
case 2: printf(" +"); break; // 2留下足迹
case 3: printf(" x"); break; // 3标记不能通过
case 8: printf("⊙"); break; // 8入口
case 9: printf("○"); break; // 9出口
}
}
printf("\n");
}
}

Status MazePath(MazeType &maze, PosType start, PosType end)
{// 若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE
LinkStack S;
SElemType e;

InitStack(S);
PosType curpos = start; // 设定“当前位置”为“入口位置”
int curstep = 1; // 探索第一步
do
{
if(Pass(maze, curpos)) // 当前可以通过,即是未曾走到过的通道块
{
FootPrint(maze, curpos); // 留下足迹
e = CreatSElem(curstep, curpos, 1); // 创建路径元素
Push(S, e); // 加入路径
if(PosEquare(curpos, end)) // 到达终点(出口)
{
StackTraverse(S, visit); // 显示所有路径
DestroyStack(S); // 销毁栈
return (TRUE); // 结束循环
}
curpos = NextPos(curpos, 1); // 下一位置是当前位置的东邻
curstep++; // 探索下一步
}
else // 当前位置不能通过
{
if(!(StackEmpty(S))) // 若栈不空,且栈顶位置尚有其它方向未经探索
{
Pop(S, e); // 则设定新的当前位置,为沿顺时针方向旋转找到的栈顶位置的下一相邻块
while(e.di == 4 && !StackEmpty(S)) // 若栈不空,但栈顶位置的四周均不可通
{
MarkPrint(maze, e.seat); // 留下不能通过的标记,并退回一步
Pop(S, e); // 删去栈顶元素
}// while
if(e.di < 4) // 若栈不空,则重新测试新的栈顶位置
{ // 直至找到一个可通的相邻块或出栈至栈空
e.di++; // 换下一个方向探索
Push(S, e); // 入栈
// e.seat通道块在迷宫中的“坐标位置” e.di从此通道块走向下一通道块的“方向”
curpos = NextPos(e.seat, e.di); // 设定当前位置是该新方向上的相邻块
}// if
}// if
}// else
}while(!StackEmpty(S));
return (FALSE);
}// MazePath 算法3.3

int main() {
// 输出迷宫数组
// 数组表头
printf(" ");
for(int i=0; i<10; i++)
printf("%2d", i);
printf("\n");
// 数组每一行
for(i=0; i<10; i++)
{
printf("%2d", i);
for(int j=0; j<10; j++)
printf("%2d", mazeArr[i][j]);
printf("\n");
}
printf("\n");

// 创建一个迷宫,并初始化
MazeType maze;
InitMaze(maze);
PrintMaze(maze); // 输出初化后的迷宫

// 设置迷宫的出入口
PosType start, end;
printf("迷宫入口坐标:1 1");
start.x = 1; start.y = 1;
printf("迷宫出口坐标:8 8");
end.x = 8; end.y = 8;
printf("\n");

// 查找迷宫路径
if(MazePath(maze, start, end))
{ // 将出入口重新标记至迷宫中
maze.arr[start.x][start.y] = 8; // 8入口
maze.arr[end.x][end.y] = 9; // 9出口

PrintMaze(maze); // 输出迷宫
}
else
{
printf("迷宫无解!");
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: