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

/* 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;                // 返回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;
}

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值