数据结构课设:迷宫问题

前言

最近刚好在写自己的课设,匆匆忙忙写出来的课设系统,仍有不足,拿出来和大家分享一下,希望能对大家有帮助。

一、概要设计

1、基本信息

具体功能包括:
(1) 选择创建的迷宫的方式:文件读取、用户输入、系统随机生成。
(2) 向用户展示生成的迷宫
(3) 选择求解的方式(DFS or BFS)
(4) 向用户展示迷宫路径
(5) 用户选择是否去掉多余路径

开发环境:VS2019

2、功能模块图

在这里插入图片描述

3、功能描述

(1) 迷宫的创建:文件读取(提前在文件中写好迷宫直接读取),用户输入(用户手动输入),系统随机生成(rand%2的方式随机生成迷宫)
(2) 求解方式:DFS(通过栈,以及DFS回溯的方式求解),BFS(通过队列,以及BFS穷举的方式求解)
(3) 去掉多余路径:多余标记全部删除

4、调用关系图

在这里插入图片描述

5、结果演示

提前在源文件同一目录下创建好这两个文件
在这里插入图片描述

① 创建迷宫

在这里插入图片描述
读取文件:
提前在文件 maze_information.txt 中写好,迷宫矩阵:
在这里插入图片描述
用户输入:
在这里插入图片描述
系统随机生成:
在这里插入图片描述

② 求解

栈+DFS:
在这里插入图片描述

队列 + BFS:
在这里插入图片描述

③ 清除多余路径

栈+DFS:
在这里插入图片描述

队列 + BFS:
在这里插入图片描述

二、完整代码

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#include<Windows.h>
#include<time.h>

typedef int Status;                     // 下一个通道方向  
#define RANGE 100                       // 迷宫大小  
#define STACK_INIT_SIZE 100             // 栈的初始大小
#define STACKINCREMENT 10               // 栈的储存增量
#define ROW 10                          // 迷宫的行数
#define COL 10                          // 迷宫的列数    

// 迷宫
typedef struct
{
    
    
    int m, n;
    int arr[RANGE][RANGE];              // 迷宫数组
}MazeType;    

//坐标(row,col)
typedef struct
{
    
    
    int row;                            // 迷宫中的行
    int col;                            // 迷宫中的列
}PosType;     

// 路径
typedef struct
{
    
    
    int step;                           // 当前位置在路径上的"序号"
    PosType seat;                       // 当前坐标
    Status di;                          // 往下一个坐标位置的方向
}SElemType;

// 定义栈
typedef struct
{
    
    
    SElemType* base;                    // 栈底
    SElemType* top;                     // 栈顶
    int stacksize;                      // 栈长
}SqStack;

// 定义队列
typedef struct
{
    
    
    PosType seat;                       // 当前坐标
    int pre;                            // 父节点
}Strnode;
Strnode que[ROW * COL];

// 原始迷宫,其中'1'表示墙,'0'表示通道
int a[ROW][COL];

// 创建迷宫
void ChoiceMazeInterface();                                                     // 选择创建方式界面
int ReadMaze(int a[ROW][COL]);                                                  // 读取文件中迷宫
int WriteMaze(int a[ROW][COL]);                                                 // 用户输入迷宫
int CreateMaze(int a[ROW][COL]);                                                // 随机迷宫
int SaveMaze(MazeType& maze);                                                  // 保存文件

// 栈的实现
int InitStack(SqStack& s);                                                      // 栈的初始化
int GetTop(SqStack s, SElemType& e);                                            // 当栈s不为空时,返回栈顶e
int Push(SqStack& s, SElemType e);                                              // 入栈
int Pop(SqStack& s, SElemType& e);                                              // 出栈
int StackEmpty(SqStack s);                                                      // 判空栈
int DestoryStack(SqStack& s);                                                   // 销毁栈

// 迷宫操作
int InitMaze(MazeType& maze, int a[ROW][COL]);                                  // 初始化迷宫
int Pass(MazeType maze, PosType curpos);                                        // 判断当前节点是否走过
int FootPrint(MazeType& maze, PosType curpos);                                  // 标记可以通过
int MarkPrint(MazeType& maze, PosType curpos);                                  // 标记不能通过
SElemType CreateSElem(int step, PosType pos, int di);                           // 创建路径
PosType NextPos(PosType curpos, Status di);                                     // 移动
int PosEqual(PosType pos1, PosType pos2);                                       // 判断是不是出口
void PrintMaze(MazeType maze, int row, int col);                                // 打印路径

// 实现方法
void Welcome();                                                                 // 迷宫路径界面
int DepthFirstInterface(MazeType& maze, PosType start, PosType end);            // 深搜界面
int DepthFirstMazePath(MazeType& maze, PosType start, PosType end);             // 深搜求解迷宫maze中,从入口start到出口end的一条路径
int BreadthFirstInterface(MazeType& maze, PosType start, PosType end);          // 广搜界面
int BreadthFirstMazePath(MazeType& maze, PosType start, PosType end);           // 广搜求解迷宫maze中,从入口start到出口end的一条路径
void Sign(MazeType& maze, int ans);                                             // 广搜标记地图
void Clean(MazeType& maze);                                                     // 清除错误路径
void GoodBye();                                                                 // 结束

int main()
{
    
    
    int i, j;
    int choice;
    PosType start, end;      //开始,终点坐标
    MazeType maze;

    while (1) 
    {
    
    
        ChoiceMazeInterface();
        scanf("%d", &choice);

        switch (choice)
        {
    
    
        case 1:
            ReadMaze(a);
            break;
        case 2:
            WriteMaze(a);
            break;
        case 3:
            CreateMaze(a);
            break;
        default:
            GoodBye();
            break;
        }

        while (1)
        {
    
    
            InitMaze(maze, a);                 // 初始化迷宫
            system("cls");
            printf("\n-------------------------------------------------\n");
            printf("\n迷宫如下:\n");
            PrintMaze(maze, ROW, COL);
            printf("\n-------------------------------------------------\n");
            printf("\n请输入迷宫起点坐标:");
            scanf("%d %d", &start.row, &start.col);
            printf("\n请输入迷宫终点坐标:");
            scanf("%d %d", &end.row, &end.col);

            Welcome();
            scanf("%d", &choice);
            switch (choice)
            {
    
    
            case 1:
                if (DepthFirstInterface(maze, start, end))
                {
    
    
                    system("cls");
                    printf("\n--------------      栈 + 深搜      --------------\n");
                    Clean(maze);
                }
                break;
            case 2:
                if (BreadthFirstInterface(maze, start, end))
                {
    
    
                    system("cls");
                    printf("\n--------------     队列 + 广搜     --------------\n");
                    Clean(maze);
                }
                break;
            case 0:
                GoodBye();
                break;
            default:
                break;
            }
            SaveMaze(maze);
            printf("\n-------------------------------------------------\n");
            printf("是否需要继续对该迷宫操作?(Yes:1 / No:0):");
            scanf("%d", &choice);
            if (choice == 0)
                break;
        }
    }
    GoodBye();
}

// 选择创建方式界面
void ChoiceMazeInterface()
{
    
    
    system("cls");
    printf("****************************************************************\n");
    printf("***********               迷宫创建                   ***********\n");
    printf("***********          1 ---- 读取文件                 ***********\n");
    printf("***********          2 ---- 用户输入                 ***********\n");
    printf("***********          3 ---- 随机生成                 ***********\n");
    printf("***********       else ---- 退出                     ***********\n");
    printf("****************************************************************\n");

    printf("请选择想要创建迷宫的方式(数字):");
}

// 读取文件
int ReadMaze(int a[ROW][COL])
{
    
    
    FILE* pfRead = fopen("maze_information.txt", "r");

    if (pfRead == NULL)
    {
    
    
        return 0;
    }

    for (int i = 1; i < ROW - 1; i++)
    {
    
    
        for (int j = 1; j < COL - 1; j++)
        {
    
    
            fscanf(pfRead, "%d", &a[i][j]);
        }
        fscanf(pfRead, "\n");
    }
    fclose(pfRead);
    return 1;
}

// 保存文件
int SaveMaze(MazeType& maze)
{
    
    
    FILE* pfRead = fopen("maze_answer.txt", "w");

    if (pfRead == NULL)
    {
    
    
        return 0;
    }

    for (int i = 1; i < ROW - 1; i++)
    {
    
    
        for (int j = 1; j < COL - 1; j++)
        {
    
    
            fprintf(pfRead, "%d", maze.arr[i][j]);
        }
        fprintf(pfRead, "\n");
    }
    fclose(pfRead);
    return 1;
}

// 用户输入迷宫
int WriteMaze(int a[ROW][COL])
{
    
    
    printf("\n-------------------------------------------------\n");
    printf("\n请输入原始迷宫:(8×8)\n");
    printf("(其中'1'表示墙,'0'表示通道)\n");
    for (int i = 1; i < ROW - 1; i++)
    {
    
    
        printf("请输入第 %d 行:", i);
        for (int j = 1; j < COL - 1; j++)
        {
    
    
            scanf("%d", &a[i][j]);
        }
    }
    return 1;
}

// 随机迷宫
int CreateMaze(int a[ROW][COL])
{
    
    
    srand(time(0));
    for (int i = 1; i < ROW - 1; i++)
    {
    
    
        for (int j = 1; j < COL - 1; j++)
        {
    
    
            if ((rand() % 100 + 1) % 2 == 0)
                a[i][j] = 0;
            else
                a[i][j] = 1;
        }
    }
    return 1;
}

// 栈的初始化
int InitStack(SqStack& s)
{
    
    
    s.base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
    if (!s.base)
        exit(0);
    s.top = s.base;
    s.stacksize = STACK_INIT_SIZE;
    return 1;
}

// 当栈s不为空时,返回栈顶e
int GetTop(SqStack s, SElemType& e)
{
    
    
    // 判空栈
    if (s.top == s.base)
        return 0;
    e = *(s.top - 1);
    return 1;
}

// 入栈
int Push(SqStack& s, SElemType e)
{
    
    
    // 判满栈
    if (s.top - s.base >= s.stacksize)
    {
    
                                     
        // 若栈满,追加存储空间
        s.base = (SElemType*)realloc(s.base, (s.stacksize + STACKINCREMENT) * sizeof(SElemType));
        if (!s.base)
            exit(0);
        s.top = s.base + s.stacksize;
        s.stacksize += STACKINCREMENT;
    }
    *s.top++ = e;
    return 1;
}

// 出栈
int Pop(SqStack& s, SElemType& e)
{
    
    
    // 判空栈
    if (s.top == s.base)
        return 0;
    e = *--s.top; //e指向新栈顶
    return 1;
}

// 判空栈
int StackEmpty(SqStack s)
{
    
    
    return s.base == s.top;
}

// 销毁栈
int DestoryStack(SqStack& s)
{
    
    
    free(&s);
    return 1;
}

// 初始化迷宫
int InitMaze(MazeType& maze, int a[ROW][COL])  
{
    
    
    // 设置迷宫maze的初值,包括加上边缘一圈的值
    int i, j;                            
    for (i = 1; i < ROW - 1;i++)
    {
    
    
        for (j = 1; j < COL - 1; j++)
        {
    
    
            maze.arr[i][j] = a[i][j];
        }
    }

    //加上围墙
    for (j = 0; j < ROW; j++)
        maze.arr[0][j] = maze.arr[ROW - 1][j] = 1;
    for (i = 0; i < COL; i++)
        maze.arr[i][0] = maze.arr[i][COL - 1] = 1;
    return 1;
}

// 判断当前节点是否走过
int Pass(MazeType maze, PosType curpos)
{
    
    
    if (maze.arr[curpos.row][curpos.col] == 0)
        return 1;
    else
        return 0;
}

// 标记可以通过
int FootPrint(MazeType& maze, PosType curpos)
{
    
    
    // 标记为2表示可以通过
    maze.arr[curpos.row][curpos.col] = 2;
    return 1;
}

// 标记不能通过
int MarkPrint(MazeType& maze, PosType curpos)
{
    
    
    // 标记为3表示不能通过
    maze.arr[curpos.row][curpos.col] = 3;
    return 1;
}

// 创建路径
SElemType CreateSElem(int step, PosType pos, int di)
{
    
    
    SElemType e;
    e.step = step;
    e.seat = pos;
    e.di = di;
    return e;
}

// 移动
PosType NextPos(PosType curpos, Status di)   
{
    
    
    // 返回当前节点的下一节点
    PosType pos = curpos;
    switch (di)
    {
    
    
    case 1:        //右
        pos.col++;
        break;
    case 2:        //下
        pos.row++;
        break;
    case 3:        //左
        pos.col--;
        break;
    case 4:        //上
        pos.row--;
        break;
    }
    return pos;
}

// 判断是不是出口
int PosEqual(PosType pos1, PosType pos2)
{
    
    
    if (pos1.row == pos2.row && pos1.col == pos2.col)
        return 1;
    else
        return 0;
}

// 打印路径
void PrintMaze(MazeType maze, int row, int col)
{
    
    
    int i, j;
    printf("  ");
    for (i = 0; i < col; i++)
        printf("%d ", i);
    printf("\n");
    for (i = 0; i < row; i++)
    {
    
    
        printf("%d ",i);
        for (j = 0; j < col; j++)
        {
    
    
            switch (maze.arr[i][j])
            {
    
    
            case 0:
                printf("  ");                        // 没走过,但是通路
                break;
            case 1:
                printf("■");                        // 墙,障碍物
                break;

            case 2:
                printf("* ");                         // 走过且走得通
                break;
            case 3:
                printf("@ ");                         // 走过但走不通,死胡同
                break;
            default:
                break;
            }
        }
        printf("\n");
    }
}

// 迷宫路径界面
void Welcome()
{
    
    
    system("cls");
    printf("****************************************************************\n");
    printf("***********               迷宫路径                   ***********\n");
    printf("***********          1 ---- 栈 + 深搜                ***********\n");
    printf("***********          2 ---- 队列 + 广搜              ***********\n");
    printf("***********          0 ---- 退出                     ***********\n");
    printf("****************************************************************\n");

    printf("请选择想要查看的方法路径(数字):");
}

// 深搜界面
int DepthFirstInterface(MazeType& maze, PosType start, PosType end) 
{
    
    
    system("cls");
    printf("\n--------------      栈 + 深搜      --------------\n");
    InitMaze(maze, a);
    // 如果找到一条路径
    if (DepthFirstMazePath(maze, start, end))
    {
    
    
        printf("\n-------------------------------------------------\n");
        printf("\n迷宫路径如下:\n");
        printf("(其中'*'表示求解路径,'@'表示死胡同)\n");
        // 输出迷宫路径
        PrintMaze(maze, ROW, COL);
        printf("\n\n");
        printf("是否需要清除错误路径?(Yes:1 / No:0):");
        int choice;
        scanf("%d", &choice);
        return choice;
    }
    // 没有通路
    else
        printf("\n--------------从入口到出口没有通路!--------------\n");
    printf("\n\n");
    return 0;
}

// 深搜求解迷宫maze中,从入口start到出口end的一条路径
int DepthFirstMazePath(MazeType& maze, PosType start, PosType end)
{
    
                                   
    SqStack s;
    SElemType e;
    InitStack(s);
    PosType curpos = start;

    // 探索第一步
    int curstep = 1;                                
    do {
    
    
        // 如果当前位置可以通过,即是未曾走到的通道块
        if (Pass(maze, curpos))
        {
    
    
            FootPrint(maze, curpos);                // 标记
            e = CreateSElem(curstep,curpos, 1);     // 创建元素
            Push(s, e);                             // 加入路径

            // 到达终点(出口)
            if (PosEqual(curpos, end))
                return 1;

            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);
                }
                if (e.di < 4)
                {
    
    
                    // 换一个方向探索
                    e.di++;
                    Push(s, e);
                    // 设定当前位置是该方向上的相邻块
                    curpos = NextPos(e.seat, e.di);
                }
            }
        }
    } while (!StackEmpty(s));
    return 0;
}

// 广搜界面
int BreadthFirstInterface(MazeType& maze, PosType start, PosType end)
{
    
    
    system("cls");
    printf("\n--------------     队列 + 广搜     --------------\n");
    InitMaze(maze, a);
    // 如果找到一条路径
    if (BreadthFirstMazePath(maze, start, end))
    {
    
    
        printf("\n-------------------------------------------------\n");
        printf("\n迷宫路径如下:\n");
        printf("(其中'*'表示求解路径,'@'表示死胡同)\n");
        // 输出迷宫路径
        PrintMaze(maze, ROW, COL);
        printf("\n\n");
        printf("是否需要清除错误路径?(Yes:1 / No:0):");
        int choice;
        scanf("%d", &choice);
        return choice;
    }
    // 没有通路
    else
        printf("\n--------------从入口到出口没有通路!--------------\n");
    printf("\n\n");
    return 0;
}

// 广搜求解迷宫maze中,从入口start到出口end的一条路径
int BreadthFirstMazePath(MazeType& maze, PosType start, PosType end)
{
    
    
    int d[4][2] = {
    
     {
    
    -1,0},{
    
    0,-1},{
    
    1,0},{
    
    0,1} }; // 控制上下左右
    int vis[ROW][COL];                           // 访问标记数组
    memset(vis, 0, sizeof(vis));                 // 初始化标记数组
    int head = 0, tail = 0, i = 0, ans;          // 队首对位,ans当前标记

    // 从起点开始
    que[tail].seat = start;
    que[tail++].pre = -1;
    vis[start.row][start.col] = 1;

    while (head < tail)
    {
    
    
        Strnode temp = que[head++];
        MarkPrint(maze, temp.seat);
        // 到达终点(出口)
        if (PosEqual(temp.seat, end))
        {
    
    
            ans = head - 1;
            Sign(maze,ans);
            return 1;
        }
        for (i = 0; i < 4; i++)
        {
    
    
            int dx = temp.seat.row + d[i][0];
            int dy = temp.seat.col + d[i][1];
            // 未访问并且通路
            if (!vis[dx][dy] && !maze.arr[dx][dy])
            {
    
    
                /*MarkPrint(maze, que[tail].seat);*/
                que[tail].seat.row = dx;
                que[tail].seat.col = dy;
                que[tail++].pre = head - 1;
                vis[dx][dy] = 1;
            }
        }
    }
    return 0;
}

// 广搜标记地图
void Sign(MazeType& maze, int ans)
{
    
    
    if (que[ans].pre != -1)
    {
    
    
        Sign(maze, que[ans].pre);
    }
    FootPrint(maze, que[ans].seat);
    return;
}

// 清除错误路径
void Clean(MazeType& maze)
{
    
    
    for (int i = 1; i < ROW - 1; i++)
    {
    
    
        for (int j = 1; j < COL - 1; j++)
        {
    
    
            if (maze.arr[i][j] == 3)
                maze.arr[i][j] = 0;
        }
    }
    printf("\n--------------     清除错误路径    --------------\n");
    printf("\n-------------------------------------------------\n");
    printf("\n迷宫路径如下:\n");
    printf("(其中'*'表示求解路径)\n");
    // 输出迷宫路径
    PrintMaze(maze, ROW, COL);
}

// 结束界面
void GoodBye() 
{
    
    
    system("cls");
    printf("ByeBye!!!\n");
    Sleep(1000);
    exit(0);
}

猜你喜欢

转载自blog.csdn.net/WZRbeliever/article/details/128493704