C数据结构与算法——队列 应用(C语言纯享版 迷宫)

实验任务

(1) 掌握顺序循环队列及其C语言的表示;

(2) 掌握入队、出队等基本算法的实现;

(3) 掌握顺序循环队列的基本应用(求解迷宫通路)。

实验内容

  • 使用C语言实现顺序循环队列的类型定义与算法函数;
  • 编写main()函数并根据需要修改、补充相关的类型定义与函数,以实现"求解迷宫通路"问题:
  • 求解迷宫通路问题描述:
    • 给定一个M×N的迷宫图,指定一个入口与一个出口;
    • 规定行走规则为:按"上右下左"优先顺序向相邻空位移动1格,用(i,j)表示迷宫中的第i行第j列的一个方块
    • 在迷宫外围加上围墙;
  • 实现指定入口和出口的固定迷宫;
  • 实现随机入口和出口的固定迷宫;
  • 实现障碍、入口和出口都随机的迷宫。

实验源码

注意:必须在Dos窗口下运行,并且以管理员身份打开Dos窗口最佳

c 复制代码
#include <stdio.h>
#include <time.h>
#include "windows.h"

#define MAXSIZE 1000
#define ROW 15
#define LINE 15
#define RATIO 0.6875 // 44/64的比例
#define COORDINATE -1 // 坐标默认 值
#define DISTOP 5 // 迷宫距离顶端距离格数

#define PASS 0 // 通路
#define WALL 1 // 墙
#define ENTRY 2 // 入口
#define EXIT 3 // 出口
#define DEAD 5 // 死路

// 延时设置
int walkDelay = 500;
int dirDelay = 1000;

typedef struct Box {
    int x;           // 点的横坐标(行)
    int y;           // 点的纵坐标(列)
    int pre;         // 上一个点的下标
} Box;

typedef struct {
    Box *base;
    int front;
    int rear;
} SqQueue;

void Map(int map[][LINE]); // 生成地图

void KnuthShuffle(int map[], int length); // 洗牌算法

void swapInt(int *a, int *b); // 辅助洗牌算法 交换

void PrintMap(int map[][LINE]); // 打印迷宫地图

boolean InitQueue(SqQueue *queue); // 循环队列的初始化

void Walk(SqQueue *queue, int in_x, int in_y, int map[][LINE]); // 移动迷宫

boolean EnQueue(SqQueue *queue, Box node); // 循环队列入队列

boolean IsFull(SqQueue *queue); // 判队满

boolean IsEmpty(SqQueue *queue); // 判队空

Box DeQueue(SqQueue *queue); // 循环队列出队列

void ShowPath(SqQueue *queue, int end); // 求解最短路径

void Color(short x); // 自定义函根据参数改变颜色

void DirTest(int map[][LINE], int dir, int j, int k); // 方向试探

void DeadPath(int j, int k); // 置为死路

void GotoXY(int x, int y); // 将光标移至屏幕 第x列,第y行 处

void DisplayQueue(SqQueue *queue); // 队列动态展示

void HideCursor(void); // 隐藏光标

/**
 * <h2>顺序队列实验</h2>
 * <h3>随机迷宫问题</h3>
 * <h3>注意:请在Dos窗口下运行</h3>
 * <h4>非循环队列,并不是真的退出队列</h4>
 * @return 0
 */
int main() {

    GotoXY(0, 0);
    Color(9);
    printf("  使用队列解决迷宫通路问题 \n");
    GotoXY(0, 1);
    printf("==============================\n");
    GotoXY(0, 2);
    Color(12);
    printf("X--走过的无效通路");
    Color(9);
    printf("  囚--起点\n");
    GotoXY(0, 3);
    Color(13);
    printf("O--走过的有效通路");
    Color(11);
    printf("  口--终点\n");
    GotoXY(0, 4);
    printf("------------------------------\n");

    srand(time(NULL));

    int map[ROW][LINE];
    Map(map);
    PrintMap(map);

    SqQueue queue;
    if (!(InitQueue(&queue))) {
        printf("队列初始化失败~~\n");
        return 0;
    }

    int in_x, in_y;
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < LINE; j++) {
            if (map[i][j] == ENTRY) {
                in_x = i;
                in_y = j;
            }
        }
    }

    HideCursor();
    DisplayQueue(&queue);
    Walk(&queue, in_x, in_y, map);

    getchar();
}

void Map(int map[][LINE]) {
    int length = (ROW - 2) * (LINE - 2); // 8 * 8 内区域
    int randArr[length];
    for (int i = 0; i < length; i++) {
        if (i == 0) {
            randArr[i++] = ENTRY;
            randArr[i++] = EXIT;
        }
        if (i < (length * RATIO) + 2) {
            randArr[i] = PASS;
        } else {
            randArr[i] = WALL;
        }
    }
    KnuthShuffle(randArr, length); // 打乱 内区域
    // 赋值整张地图
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < LINE; j++) {
            // 这里一个小技巧:只要前面四个表达式一个为假,说明未到边界赋值,保证Length不会越界
            if (i != 0 && i != ROW - 1 && j != 0 && j != LINE - 1 && length--) {
                map[i][j] = randArr[length];
            } else {
                map[i][j] = WALL;
            }
        }
    }
}

void KnuthShuffle(int map[], int length) {
    for (int i = length - 1; i >= 1; i--) {
        swapInt(&map[i], &map[rand() % (i + 1)]);
    }
}

void swapInt(int *a, int *b) {
    int t;
    t = *a;
    *a = *b;
    *b = t;
}

void PrintMap(int map[][LINE]) {
    for (int i = 0; i < ROW; i++) {
        for (int j = 0; j < LINE; j++) {
            GotoXY(j * 2, i + DISTOP);
            switch (map[i][j]) {
                case PASS:
                    printf(" ");
                    break;
                case WALL:
                    Color(10);
                    printf("围");
                    break;
                case ENTRY:
                    Color(9);
                    printf("囚");
                    break;
                case EXIT:
                    Color(11);
                    printf("口");
                    break;
            }
        }
        printf("\n");
    }
    Sleep(3000);
}

boolean InitQueue(SqQueue *queue) {
    queue->base = (Box *) malloc(sizeof(Box) * MAXSIZE);
    if (!(queue->base)) {
        return FALSE;
    }
    queue->front = queue->rear = 0;
    return TRUE;
}

void Walk(SqQueue *queue, int in_x, int in_y, int map[][LINE]) {
    // 起点先入队列
    Box node; // 生成当前位置(起点)
    node.x = in_x;
    node.y = in_y;
    node.pre = COORDINATE; // 起点位置下标 -1

    EnQueue(queue, node); // 起点入队列

    while (!(IsEmpty(queue))) { // 无路可走的情况,回到起点
        node = DeQueue(queue); // 取出队头位置 队头指针front++

        if (map[node.x][node.y] == EXIT) { // 判断当前位置是否是终点
            ShowPath(queue, queue->front);
            return;
        }
        int dir; // 装方向
        Box tNode; // 生成下一个位置
        for (dir = 0; dir < 4; dir++) { // 判断当前位置各个方向是否可走
            switch (dir) {
                case 0:
                    tNode.x = node.x - 1;
                    tNode.y = node.y;
                    DirTest(map, dir, node.x, node.y);
                    break;
                case 1:
                    tNode.x = node.x;
                    tNode.y = node.y + 1;
                    DirTest(map, dir, node.x, node.y);
                    break;
                case 2:
                    tNode.x = node.x + 1;
                    tNode.y = node.y;
                    DirTest(map, dir, node.x, node.y);
                    break;
                case 3:
                    tNode.x = node.x;
                    tNode.y = node.y - 1;
                    DirTest(map, dir, node.x, node.y);
                    break;
            }
            if (map[tNode.x][tNode.y] == PASS || map[tNode.x][tNode.y] == EXIT) { // 判断这个方向 是否可走
                tNode.pre = queue->front - 1; // 把节点位置的下标给 新位置
                EnQueue(queue, tNode); // 入队
                if (map[tNode.x][tNode.y] == PASS) {
                    map[tNode.x][tNode.y] = DEAD;
                    DeadPath(tNode.x, tNode.y);

                }
            }
        }
    }
    // 这里加二号条件的原因是:此程序使用的是终点出队列即停止,但是也不排除 到终点即为空
    if (IsEmpty(queue) && map[node.x][node.y] != EXIT) {
        GotoXY(0, ROW + DISTOP + 2);
        Color(12);
        printf("\t无路可走,死翘翘了~~\n");
    }
}

boolean EnQueue(SqQueue *queue, Box node) {
    if (IsFull(queue)) {
        return FALSE;
    }
    queue->base[queue->rear] = node; // 新元素插入队尾
    queue->rear = queue->rear + 1; // 队尾指针加 1
    DisplayQueue(queue);
    return TRUE;
}

boolean IsFull(SqQueue *queue) {
    return queue->rear + 1 == queue->front; // 非循环队列
}

boolean IsEmpty(SqQueue *queue) {
    return queue->rear == queue->front;
}

Box DeQueue(SqQueue *queue) {
    Box box = queue->base[queue->front++];
    DisplayQueue(queue);
    return box; // 取出队头元素,并把其出队列
}

void ShowPath(SqQueue *queue, int end) {
    int i, tmp;
    for (i = end - 1; i >= 0;) {
        tmp = queue->base[i].pre;
        queue->base[i].pre = COORDINATE;
        i = tmp;
    }
    int count = 0, ti;
    for (i = 1; i < end; i++) { // i = 1, 保证不是终点即可
        if (queue->base[i].pre == COORDINATE) {
            if (count == 0) {
                GotoXY(LINE * 2 + 35, DISTOP - 1);
                printf("↓ 路径打印 ↓");
                GotoXY(LINE * 2 + 35, DISTOP);
                printf("|__i__j__pre__|");
            }
            count++;
            GotoXY(LINE * 2 + 35, DISTOP + count);
            printf("|_____________|\n");
            Color(11);
            GotoXY(LINE * 2 + 35 + 3, DISTOP + count);
            printf("%d", queue->base[i].x);
            GotoXY(LINE * 2 + 35 + 7, DISTOP + count);
            printf("%d", queue->base[i].y);
            GotoXY(LINE * 2 + 35 + 10, DISTOP + count);
            printf("%d", queue->base[i].pre);
            if (count == 1) {
                ti = i;
                continue;
            }
            GotoXY(queue->base[ti].y * 2, queue->base[ti].x + DISTOP);
            Color(15);
            if (queue->base[i].x - queue->base[ti].x == -1 &&
                queue->base[i].y - queue->base[ti].y == 0) {
                printf("↑");
            } else if (queue->base[i].x - queue->base[ti].x == 0 &&
                       queue->base[i].y - queue->base[ti].y == 1) {
                printf("→");
            } else if (queue->base[i].x - queue->base[ti].x == 1 &&
                       queue->base[i].y - queue->base[ti].y == 0) {
                printf("↓");
            } else {
                printf("←");
            }
            ti = i;
        }
    }
}

void Color(short x) {
    if (x >= 0 && x <= 15) { // 参数在0-15的范围颜色
        SetConsoleTextAttribute( // 调用设置控制台文本属性函数(调用获取句柄函数(不理解), 不理解)
                GetStdHandle(STD_OUTPUT_HANDLE), x);    // 只有一个参数,改变字体颜色
    } else { // 默认的颜色白色
        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
    }
}

void DirTest(int map[][LINE], int dir, int j, int k) {
    GotoXY(k * 2, j + DISTOP);
    Color(15);
    switch (dir) {
        case 0:
            printf("↑");
            break;
        case 1:
            printf("→");
            break;
        case 2:
            printf("↓");
            break;
        case 3:
            printf("←");
            break;
    }
    Sleep(dirDelay);
    GotoXY(k * 2, j + DISTOP);
    Color(13);
    switch (map[j][k]) {
        case ENTRY:
            Color(9);
            printf("囚");
            break;
        case DEAD:
            Color(12);
            printf("X");
            break;
    }
}

void DeadPath(int j, int k) {
    GotoXY(k * 2, j + DISTOP);
    Color(12);
    printf("X");
    Sleep(walkDelay);
}

void GotoXY(int x, int y) {
    COORD pos = {x, y}; // 坐标
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); // 获取句柄(标准输出句柄)
    SetConsoleCursorPosition(hOut, pos); // 设置控制台光标位置
}

void DisplayQueue(SqQueue *queue) {
    int len = ROW - 1;
    Color(12);
    GotoXY(LINE * 2 + 10, DISTOP);
    printf("|__i__j__di__| <- top");
    for (int j = 1; j <= len; j++) {
        GotoXY(LINE * 2 + 10, DISTOP + j);
        printf("|____________|\n");
    }
    int length = queue->rear;
    for (int i = 0; i < length; i++, len--) {
        if (len == 0) {
            len = ROW - 1;
            for (int j = 1; j <= len; j++) {
                GotoXY(LINE * 2 + 10, DISTOP + j);
                printf("|____________|\n");
            }
        }
        Color(11);
        GotoXY(LINE * 2 + 10 + 3, DISTOP + len);
        printf("%d", queue->base[i].x);
        GotoXY(LINE * 2 + 10 + 7, DISTOP + len);
        printf("%d", queue->base[i].y);
        GotoXY(LINE * 2 + 10 + 10, DISTOP + len);
        printf("%d", queue->base[i].pre);
    }
}

void HideCursor(void) {
    CONSOLE_CURSOR_INFO cursor_info = {1, 0};
    SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cursor_info);
}

实验结果

相关推荐
HappyAcmen1 分钟前
青训营-豆包MarsCode技术训练营试题解析四十八
开发语言·python·算法
新知图书3 分钟前
微软Win11内核迎新变,Rust语言助力提升系统安全可靠性
开发语言·microsoft·rust
Zer0_on3 分钟前
数据结构二叉树
开发语言·数据结构
洛洛英英8 分钟前
遇到duilib.dll丢失怎么办?解决duilib.dll文件丢失问题的全面指南
经验分享·电脑
码农老起8 分钟前
插入排序解析:时间复杂度、空间复杂度与优化策略
数据结构·算法·排序算法
SomeB1oody12 分钟前
【Rust自学】7.4. use关键字 Pt.2 :重导入与换国内镜像源教程
开发语言·后端·rust
新手小袁_J15 分钟前
No Python at ‘C:\Users\MI\AppData\Local\Programs\Python\Python39\python.exe‘
开发语言·python·error·no python
stormjun15 分钟前
基于 Python Django 的二手电子设备交易平台(附源码,文档)
开发语言·python·django·二手电子设备·电子设备售卖·电子设备交易
新知图书17 分钟前
Rust编程与项目实战-箱
开发语言·后端·rust
俎树振20 分钟前
深入理解与优化Java二维数组:从定义到性能提升的全面指南
java·算法