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

问题描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

源码如下:


#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 (int 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 (int 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;
}

运行截图:

在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值