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

news2024/10/10 12:16:51

实验任务

(1) 掌握顺序栈及其C语言的表示;
(2) 掌握入栈、出栈等基本算法的实现;
(3) 掌握顺序栈的基本应用(求解迷宫通路)。

实验内容

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

实验源码

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

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <conio.h>
#include "windows.h"

#define MAXSIZE 1000
#define RATIO 0.6875 // 44/64的比例
#define DFDIR -1 // 方向默认 值
#define DISTOP 8 // 迷宫距离顶端距离格数

#define PASS 0 // 通路
#define WALL 1 // 墙
#define ENTRY 2 // 入口
#define EXIT 3 // 出口
// 可走
#define WALKU 40 // ↑
#define WALKR 41 // →
#define WALKD 42 // ↓
#define WALKL 43 // ←
#define DEAD 5 // 死路

// 延时设置
int walkDelay = 10;
int dirDelay = 10;
// 迷宫大小设置
int row = 20;
int line = 20;  // 这里有一个DOS窗口BUG,未解决

typedef struct {
    int x, y;
    int dir; // 方向
} Node;
typedef struct {
    int top;
    Node *data;
} SqStack;

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

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

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

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

boolean InitStack(SqStack *stack); // 初始化

void Walk(SqStack *stack, int in_x, int in_y, int map[][line]); // 移动迷宫

boolean Push(SqStack *stack, Node node); // 压栈

boolean IsFull(SqStack *stack); // 判栈满

boolean IsEmpty(SqStack *stack); // 判栈空

Node GetTop(SqStack *stack); // 取栈顶元素

void Pop(SqStack *stack); // 出栈 (这里用void是以下代码中保证无需判断)

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

void WalkPath(int map[][line], int dir, int j, int k); // 走过的路径方向设置

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

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

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

void DisplayStack(SqStack *stack); // 栈动态展示

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

void MazeSize(int key, int map[][line]); // 迷宫大小

void DelaySet(int key, int map[][line]); // 延时设置

void UI(); // 界面设计

/**
 * <h2>顺序栈实验</h2>
 * <h3>随机迷宫问题</h3>
 * <h3>注意:请在Dos窗口↓运行</h3>
 * @return 0
 */
int main() {

    while (1) {
        UI(); // 界面打印

        srand(time(NULL));

        int map[row][line];
        char key;
        MazeSize(key, map); // 迷宫大小
        DelaySet(key, map); // 延时设置

        SqStack stack;
        if (!(InitStack(&stack))) {
            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();
        DisplayStack(&stack);
        Sleep(3000);
        Walk(&stack, in_x, in_y, map);
        if (IsEmpty(&stack)) {
            GotoXY(0, row + DISTOP + 2);
            printf("无路可走,死翘翘了~~\n");
        }

        char quit;
        GotoXY(0, row + DISTOP + 4);
        printf("任意键继续(q退出):");
        quit = getch();
        if (quit == 'q') {
            break;
        } else {
            system("cls");
        }

    }

    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");
    }
}

boolean InitStack(SqStack *stack) {
    stack->data = malloc(sizeof(Node) * MAXSIZE);
    if (!(stack->data)) {
        return FALSE;
    }
    stack->top = 0;
    return TRUE;
}

void Walk(SqStack *stack, int in_x, int in_y, int map[][line]) {
    // 起点先入栈,为后续循环做铺垫
    Node node; // 生成当前位置(起点)
    node.x = in_x;
    node.y = in_y;
    node.dir = DFDIR;
    // 无方向
    Push(stack, node); // 起点入栈

    while (!(IsEmpty(stack))) { // 无路可走的情况,回到起点
        node = GetTop(stack); // 取出当前位置
        if (map[node.x][node.y] == EXIT) { // 判断当前位置是否是终点
            break;
        } else { // 如果不是终点,继续走
            int i, j, k;
            for (i = node.dir + 1; i < 4; i++) { // 判断当前位置各个方向是否可走
                switch (i) {
                    case 0: // 上
                        j = node.x - 1;
                        k = node.y;
                        DirTest(map, i, node.x, node.y);
                        break;
                    case 1: // 右
                        j = node.x;
                        k = node.y + 1;
                        DirTest(map, i, node.x, node.y);
                        break;
                    case 2: // 下
                        j = node.x + 1;
                        k = node.y;
                        DirTest(map, i, node.x, node.y);
                        break;
                    case 3: // 左
                        j = node.x;
                        k = node.y - 1;
                        DirTest(map, i, node.x, node.y);
                        break;
                }
                if (map[j][k] == PASS || map[j][k] == EXIT) { // 判断这个方向 是否可走
                    Node tNode; // 生成下一个位置
                    tNode.x = j;
                    tNode.y = k;
                    tNode.dir = DFDIR;

                    stack->data[stack->top - 1].dir = i; // 记录当前位置行走方向
                    Push(stack, tNode); // 下一个位置入栈
                    if (map[node.x][node.y] != WALL && map[node.x][node.y] != EXIT
                        && map[node.x][node.y] != ENTRY) {
                        WalkPath(map, i, node.x, node.y); // 将当前位置置为下一个要走的位置的方向
                    }
                    break; // 退出这个位置的方向查找循环
                }
            }
            if (i == 4) { // 四个方向都判断完,但是走不通
                if (!(map[node.x][node.y] == ENTRY)) { // 保证起点不被置为ENTRY
                    map[node.x][node.y] = DEAD; // 把当前位置,记为死路
                    DeadPath(node.x, node.y);
                }
                Pop(stack); // 当前位置出栈
            }
        }
    }
}

boolean Push(SqStack *stack, Node node) {
    if (IsFull(stack)) {
        return FALSE;
    }
    stack->data[stack->top++] = node;
    DisplayStack(stack);
    return TRUE;
}

boolean IsFull(SqStack *stack) {
    return stack->top == MAXSIZE;
}

boolean IsEmpty(SqStack *stack) {
    return stack->top == 0;
}

Node GetTop(SqStack *stack) {
    return stack->data[stack->top - 1];
}

void Pop(SqStack *stack) {
    stack->top--;
    DisplayStack(stack);
}

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

void WalkPath(int map[][line], int dir, int j, int k) {
    GotoXY(k * 2, j + DISTOP);
    Color(13);
    switch (dir) {
        case 0:
            printf("↑");
            map[j][k] = WALKU;
            break;
        case 1:
            printf("→");
            map[j][k] = WALKR;
            break;
        case 2:
            printf("↓");
            map[j][k] = WALKD;
            break;
        case 3:
            printf("←");
            map[j][k] = WALKL;
            break;
    }
    Sleep(walkDelay);
}

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

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 WALKU:
            printf("↑");
            break;
        case WALKR:
            printf("→");
            break;
        case WALKD:
            printf("↓");
            break;
        case WALKL:
            printf("←");
            break;
    }
}

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

void DisplayStack(SqStack *stack) {
    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 = stack->top;
    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", stack->data[i].x);
        GotoXY(line * 2 + 10 + 7, DISTOP + len);
        printf("%d", stack->data[i].y);
        GotoXY(line * 2 + 10 + 10, DISTOP + len);
        printf("%d", stack->data[i].dir);
    }
}

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 MazeSize(int key, int map[][line]) {
    do {
        do {
            UI();
            GotoXY(0, 5);
            printf("迷宫高度(a↑ d↓):%d 格", row);
            key = getch();
            if (key == 'a') {
                row++;
            }
            if (key == 'd') {
                row--;
            }
            system("cls");
            Map(map); // 生成地图
            PrintMap(map); // 打印初始地图
        } while (key == 'a' || key == 'd');
        do {
            UI();
            GotoXY(0, 5);
            printf("迷宫高度(a↑ d↓):%d 格", row);
            GotoXY(0, 6);
            printf("迷宫宽度(a↑ d↓):%d 格", line);
            key = getch();
            if (key == 'a') {
                line++;
            }
            if (key == 'd') {
                line--;
            }
            system("cls"); // 清屏
            Map(map); // 生成地图
            PrintMap(map); // 打印初始地图
        } while (key == 'a' || key == 'd');
    } while (row < 4 || line < 4 || row > 20 || line > 20);
}

void DelaySet(int key, int map[][line]) {
    do {
        do {
            UI();
            GotoXY(0, 5);
            printf("行走延时(a↑ d↓):%d ms", walkDelay);
            key = getch();
            if (key == 'a') {
                walkDelay += 10;
            }
            if (key == 'd') {
                walkDelay -= 10;
            }
            system("cls");
            PrintMap(map);
        } while (key == 'a' || key == 'd');
        do {
            UI();
            GotoXY(0, 5);
            printf("行走延时(a↑ d↓):%d ms", walkDelay);
            GotoXY(0, 6);
            printf("方向延时(a↑ d↓):%d ms", dirDelay);
            key = getch();
            if (key == 'a') {
                dirDelay += 10;
            }
            if (key == 'd') {
                dirDelay -= 10;
            }
            system("cls");
            PrintMap(map);
        } while (key == 'a' || key == 'd');
    } while (dirDelay < 0 || walkDelay < 0);
    UI();
    GotoXY(0, 5);
    printf("行走延时(a↑ d↓):%d ms", walkDelay);
    GotoXY(0, 6);
    printf("方向延时(a↑ d↓):%d ms", dirDelay);
}

void UI() {
    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");
    GotoXY(0, 7);
    printf("------------------------------\n");
}

/*
解析一波:
Color(0);	printf("黑色\n");
Color(9); 	printf("蓝色\n");
Color(2); 	printf("绿色\n");
Color(3); 	printf("湖蓝色\n");
Color(4);  	printf("红色\n");
Color(5);  	printf("紫色\n");
Color(11); 	printf("黄色\n");
Color(5);  	printf("白色\n");
Color(8);  	printf("灰色\n");
Color(9); 	printf("淡蓝色\n");
Color(2); 	printf("淡绿色\n");
Color(11); 	printf("淡浅绿色\n");
Color(12);  printf("淡红色\n");
Color(13); 	printf("淡紫色\n");
Color(14); 	printf("淡黄色\n");
Color(15); 	printf("亮白色\n");
Color(16);    // 因为这里大于15,恢复默认的颜色
 */

实验结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/790327.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

❓“如何创业?互联网创业又该如何入手?

&#x1f31f;5大创业建议&#xff0c;让你轻松入门&#xff01; 作为一名互联网创业者&#xff0c;我想分享一下我的创业经验。下面是我的五个建议&#xff0c;希望对你有所帮助&#xff01; &#x1f31f;了解市场需求 在创业之前&#xff0c;了解市场需求非常重要。你需要研…

【Rust日报】2023-07-24 使用 Rust 重写的InfluxDB 3.0

使用 Rust 重写的InfluxDB 3.0 InfluxDB 是一个开源的、分布式的时序数据库&#xff0c;用于存储和分析时间序列数据, 于 2013 年由 Brian Bondy 和 Nicholas Zakhariev 创立。 InfluxDB 最初是用 Go 语言编写的。 2018 年&#xff0c;InfluxData 决定将 InfluxDB 重写为 Rust …

kotlin 编写一个简单的天气预报app(一)

使用Android Studio开发天气预报APP 今天我来分享一下如何使用Android Studio开发一个天气预报APP。在文中&#xff0c;我们将使用第三方接口获取实时天气数据&#xff0c;并显示在APP界面上。 步骤一&#xff1a;创建新项目 首先&#xff0c;打开Android Studio并创建一个新…

Flutter 最佳实践和编码准则

Flutter 最佳实践和编码准则 视频 前言 最佳实践是一套既定的准则&#xff0c;可以提高代码质量、可读性和可靠性。它们确保遵循行业标准&#xff0c;鼓励一致性&#xff0c;并促进开发人员之间的合作。通过遵循最佳实践&#xff0c;代码变得更容易理解、修改和调试&#xff…

【如何训练一个中译英翻译器】LSTM机器翻译seq2seq字符编码(一)

系列文章 【如何训练一个中译英翻译器】LSTM机器翻译seq2seq字符编码&#xff08;一&#xff09; 【如何训练一个中译英翻译器】LSTM机器翻译模型训练与保存&#xff08;二&#xff09; 【如何训练一个中译英翻译器】LSTM机器翻译模型部署&#xff08;三&#xff09; 【如何训…

新能源电动车充电桩控制主板的技术

新能源电动车充电桩控制主板的技术 你是否曾经遇到过电动车行驶到一半没电的情况?这不仅尴尬&#xff0c;还可能对你的生活造成困扰。然而&#xff0c;随着充电桩主板技术的出现&#xff0c;这个问题得到了有效的解决。那么&#xff0c;这个技术到底包括哪些方面呢?让我们一起…

IDEA代码自动格式化工具

1.自动import 在IDEA中&#xff0c;打开 IDEA 的设置&#xff0c;找到 Editor -> General -> Auto Import。勾选上 Add unambiguous imports on the flyOptimize imports on the fly (for current project) 2.gitee 提交格式化 设置方法如下: 1.打开设置 2.找到版本…

如何写好测试报告?

目录 一、目标 二、模板的使用 三、修订记录 四、内容应该清晰易懂&#xff0c;简明扼要 五、绝不放过一个错字 六、遗留问题单 七、产出成果恰当呈现 一、目标 本文介绍测试人员编写软件测试报告常见的疏漏&#xff0c;以便大家避免&#xff0c;更好让测试成果呈现给客…

Kotlin 协程 CoroutineScope

协程定义&#xff1a; 19年官方是这样说的&#xff1a;协程是轻量级的线程&#xff0c;协程就是 Kotlin 提供的一套线程封装的 API&#xff1b; 现在官方是这样说的&#xff1a;协程是一种并发设计模式&#xff1b; 协程作用&#xff1a; 1.处理耗时任务&#xff1b; 2.保…

【雕爷学编程】Arduino动手做(172)---WeMos D1开发板模块

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

RL 实践(4)—— 二维滚球环境【DQN Double DQN Dueling DQN】

本文介绍如何用 DQN 及它的两个改进 Double DQN & Dueling DQN 解二维滚球问题&#xff0c;这个环境可以看做 gym Maze2d 的简单版本参考&#xff1a;《动手学强化学习》完整代码下载&#xff1a;5_[Gym Custom] RollingBall (DQN and Double DQN and Dueling DQN) 文章目录…

智能喷涂机器人的制作分享

作者&#xff1a;朱家谊、吾丽江、管孝天 单位&#xff1a;天津工业大学 指导老师&#xff1a;李鹏 1. 概念说明 智能喷涂机器人是一种具有自主感知、决策和执行能力的机器人&#xff0c;专门用于自动化喷涂任务&#xff0c;它可以应用于各种领域&#xff0c;如汽车制造、建…

【已解决】jupyter notebook里已经安装了第三方库,还是提示导入失败

在jupyter notebook中运行Python代码&#xff0c;明明已经安装了第三方库&#xff0c;还是提示导入失败。 以导入pandas库为例&#xff0c;其他库同理&#xff1a; 报错代码&#xff1a; import pandas报错原因&#xff1a; 电脑上存在多个python运行环境&#xff08;比如&a…

JavaScript学习 -- Hex编码

Hex编码是一种十六进制数字的表示方式。在JavaScript中&#xff0c;我们可以使用Hex编码来表示数字、颜色和其他二进制数据&#xff0c;并将其用于各种场景&#xff0c;例如Web开发、图像处理和加密解密等。在本篇博客中&#xff0c;我们将介绍Hex编码的基础知识和相关技术&…

Xilinx FPGA平台GTX简易使用教程(汇总篇)

GTX简易使用教程&#xff0c;先“知其然”&#xff0c;慢慢再研究“所以然”。 目录 一、GTX必备基础知识 二、时钟篇 三、复位与初始化 四、GTX IP核配置介绍 五、GTX收发测试 六、后记 一、GTX必备基础知识 虽说搬砖只需要会用IP就行&#xff0c;但是为了把砖搬好&a…

js:浏览器环境下复制图片到剪切板

浏览器环境下复制图片到剪切板思路&#xff1a; 通过canvas将图片url转为base64格式将base64格式转为Blob类型的数据调用浏览器接口复制内容到剪切板 图片处理工具方法 image-util.js // Image对象转base64 export function imageToBase64(image) {let canvas document.cr…

Canal深入调研

Canal深入调研 1.canal的设计 1.1 Canal的设计理念 canal的组件化设计非常好&#xff0c;有点类似于tomcat的设计。使用组合设计&#xff0c;依赖倒置&#xff0c;面向接口的设计。 说明&#xff1a; ​ server代表一个canal运行实例&#xff0c;对应于一个jvm ​ instance…

免费分享一套基于SpringBoot实现商城系统(仿天猫),挺漂亮的

大家好&#xff0c;我是锋哥&#xff0c;看到一个不错的基于SpringBoot实现商城系统(仿天猫)系统&#xff0c;分享下哈。 项目介绍 迷你天猫商城是一个基于Spring Boot的综合性B2C电商平台&#xff0c;需求设计主要参考天猫商城的购物流程&#xff1a;用户从注册开始&#xf…

编码器原理

编码器原理 编码器是一种用来测量机械旋转或位移的传感器。这种传感器能够测量机械部件在旋转或直线运动时的位移位置或速度等信息,并将其转换成一系列电信号。 光栅式旋转编码器 霍尔式编码器

制作一个简易的计算器app

1、Ui开发 笔者的Ui制作的制作的比较麻烦仅供参考&#xff0c;在这里使用了多个LinearLayout对屏幕进行了划分。不建议大家这样做最好使用GridLayout会更加快捷简单 笔者大致划分是这样的&#xff1a; 使用了四个大框&#xff0c;在第四个大框里面有多个小框 最终界面如下&am…