【实验目的】
- 加强程序设计和算法应用的能力,
- 熟练掌握链表与栈的基本操作。
- 学会分析问题,提高解决问题的效率。
【实验原理】
- 用二维数组来表示迷宫,生成随机数后进行%2 运算,用 PATH 表示通路(值为 0) ,
用 WALL 表示围墙(值为 1)。 - 用链栈储存当前的位置 x,y 和当前的方向 dir。
- 由用户输入迷宫的大小、入口和出口。
- 用一个 Direction 函数来寻找方向,按照上下左右的顺序依次搜索,返回值即为当
前方向。 - 用 FindWay 函数生成通往出口的路线,并标记走过的位置为 PAST(值为-1),死路
为 DEAD(值为 2)
【实验内容】
-
问题描述
以一个 m ∗ n m*n m∗n的长方阵表示迷宫,0 和 1 分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结
论。 -
基本要求
(1)首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递 归程序。求得的通路以三元组 ( i , j , d ) (i, j, d) (i,j,d)的形式输出。其中: ( i , j ) (i, j) (i,j)指示 迷宫中的一个坐标, d d d表示走到下一坐标的方向。
(2)编写递归形式的算法,求得迷宫中所有可能的通路。
(3)以方阵形式输出迷宫及其通路。 -
重点、难点
重点:针对迷宫问题的特点,利用栈的后进先出特点,选择适当的数据结构。 难点:递归算法的设计与求解。
【程序代码与运行结果】
- StackDefine.h头文件
//StackDefine.h 链栈的定义
#pragma once
#include <iostream>
#include <stdlib.h>
#define UP 3 //上
#define UNDER 4 //下
#define LEFT 5 //左
#define RIGHT 6 //右
#define OK 1
#define ERROR 0
using namespace std;
typedef int Status;
//以链栈存储所走路径中每个点的坐标与方向
typedef struct StackNode
{
int x, y; //当前坐标(x,y)
int dir; //当前方向
struct StackNode* next;
}StackNode, *LinkStack;
Status InitStack(LinkStack &S);
Status Push(LinkStack &S, int ey, int ex, int ed);
Status Pop(LinkStack &S, int &ey, int &ex, int &ed);
Status ClearStack(LinkStack &S);
void Trans(LinkStack &A, LinkStack &B);
void PrintStack(LinkStack S);
- MazeDefine.h头文件
//MazeDefine.h:迷宫的定义以及生成方法
#pragma once
#include <Windows.h>
#include <time.h>
#define MAXSIZE 20//迷宫最大长度和宽度
#define PAST -1 //走过的路线
#define PATH 0 //通路
#define WALL 1 //墙
#define DEAD 2 //死路
int x, y; //当前位置
int dir; //当前方向
int m, n; //用户定义地图大小
int beginy, beginx, endy, endx;//起点终点坐标
int Maze[MAXSIZE][MAXSIZE] = {};//开辟地图空间
//由用户输入迷宫长宽创建随机迷宫
void CreateMaze()
{
do
{
cout << "请依次输入迷宫的长x和宽y,其中4≤x,y≤20:";
cin >> m >> n;
if (m < 4 || m>MAXSIZE || n < 4 || n>MAXSIZE)
{
cout << "规定4≤x,y≤20,请重新输入!" << endl;//错误提示
}
} while (m < 4 || m>MAXSIZE || n < 4 || n>MAXSIZE);
srand((unsigned)time(NULL)); //用时间做随机数种子
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
{
//最外层一圈设为围墙,防止越界
if (i == 0 || j == 0 || i == n - 1 || j == m - 1)
Maze[i][j] = WALL;
//围墙内随机生成迷宫
else
Maze[i][j] = rand() % 2;
}
}
void PrintMaze();//打印迷宫
void InputBeginAndEnd();//设置入口与出口
Status Direction(LinkStack &L);//判定方向
void FindWay(LinkStack &L); //寻找路径
- StackOperation.cpp源文件
//StackOperation.cpp:栈的操作
#include "StackDefine.h"
StackNode *p;
//初始化栈
Status InitStack(LinkStack &S)
{
S = NULL;
return OK;
}
//入栈操作
Status Push(LinkStack &S, int ex, int ey, int ed)
{
p = new StackNode;
p->y = ey;
p->x = ex;
p->dir = ed;
p->next = S;
S = p;
return OK;
}
//出栈操作
Status Pop(LinkStack &S, int &ex, int &ey, int &ed)
{
if (S == NULL) return ERROR;
ey = S->y;
ex = S->x;
ed = S->dir;
p = S;
S = S->next;
delete p;
return OK;
}
//清空栈
Status ClearStack(LinkStack &S)
{
StackNode *q;
p = S;
while (p)
{
q = p;
p = p->next;
free(q);
}
S = NULL;
return OK;
}
//将栈A元素依次出栈并入B栈
void Trans(LinkStack &A, LinkStack &B)
{
while (A)
{
int tempy, tempx, tempd;
Pop(A, tempy, tempx, tempd);
Push(B, tempy, tempx, tempd);
}
}
//打印栈中元素
void PrintStack(LinkStack S)
{
if (!S) return;
cout << " (" << S->x << "," << S->y;
switch (S->dir)
{
case UP: cout << ",↑"; break;
case UNDER: cout << ",↓"; break;
case LEFT: cout << ",←"; break;
case RIGHT: cout << ",→"; break;
}
cout << ")";
if (S->dir != 0)cout << " ==>";
PrintStack(S->next);
}
- Maze(main).cpp源文件
//main.cpp:迷宫求解方法以及主函数
#include "StackDefine.h"
#include "MazeDefine.h"
//打印迷宫
void PrintMaze()
{
for (int i = n - 1; i >= 0; i--)
{
if (i < 10) cout << ' ' << i;
else cout << i;
for (int j = 0; j < m; j++)
{
if (j == beginx && i == beginy) {
cout << "入";
continue;
}
if (j == endx && i == endy) {
cout << "出";
continue;
}
switch (Maze[i][j])
{
case PATH:cout << " "; break;
case WALL:cout << "□"; break;
case PAST:cout << "■"; break;
case DEAD:cout << "×"; break;
}
}
cout << endl;
}
cout << " ";
for (int j = 0; j < m; j++)
{
if (j < 10)
cout << " " << j;
else cout << j;
}
cout << endl;
}
//设置入口与出口
void InputBeginAndEnd()
{
do
{
cout << "请选择图中不是墙的点作为入口并输入该点横坐标与纵坐标,用空格隔开:";
cin >> beginx >> beginy;
x = beginx, y = beginy;
cout << "请选择图中不是墙的点作为出口并输入该点横坐标与纵坐标,用空格隔开:";
cin >> endx >> endy;
bool judge = (Maze[beginy][beginx] != PATH) || (Maze[endy][endx] != PATH) || (beginy == endy && beginx == endx);
if (judge)
{
cout << "输入有误,请重新输入!" << endl;//错误提示
}
} while ((Maze[beginy][beginx] != PATH) || (Maze[endy][endx] != PATH) || (beginy == endy && beginx == endx));
}
//方向判定
Status Direction(LinkStack &L)
{
//选择可以移动的方向
if (Maze[y + 1][x] == PATH) return UP;
else if (Maze[y - 1][x] == PATH) return UNDER;
else if (Maze[y][x - 1] == PATH) return LEFT;
else if (Maze[y][x + 1] == PATH) return RIGHT;
//如果上下左右都是墙或死路
else if ((Maze[y + 1][x] == WALL || Maze[y + 1][x] == DEAD) &&
(Maze[y - 1][x] == WALL || Maze[y - 1][x] == DEAD) &&
(Maze[y][x - 1] == WALL || Maze[y][x - 1] == DEAD) &&
(Maze[y][x + 1] == WALL || Maze[y][x + 1] == DEAD))
{
cout << "该迷宫无法到达出口!";
system("pause");
exit(0);
}
//原路返回
else
{
Maze[y][x] = DEAD; //把这里标记成死路
Pop(L, x, y, dir); //当前坐标转化为上一个走过的点
Direction(L); //递归下去直到找到方向,或找不到方向退出
}
}
//非递归寻找出口
void FindWay(LinkStack &L)
{
while (true)
{
//若到达终点
if (y == endy && x == endx)
{
dir = 0;
Push(L, x, y, dir);
return;
}
//若四周不存在终点,则继续寻找出口
else if (UP < (dir = Direction(L)) <= RIGHT)
{
//找到方向,走一步
Push(L, x, y, dir); //当前坐标入栈
Maze[y][x] = PAST; //记录已走过的位置
switch (dir)
{
case UP:y++; break;
case UNDER:y--; break;
case LEFT:x--; break;
case RIGHT:x++; break;
}
}
system("cls");
PrintMaze();
Sleep(100);
}
}
//主函数
int main()
{
LinkStack L, M; //声明两个栈,栈L用于存储坐标和方向,栈M是用于倒置输出L的辅助栈
InitStack(L), InitStack(M);
while (true)
{
system("cls");
beginy = -1, beginx = -1, endy = -1, endx = -1; //刷新起点终点坐标,防止被打印出来
ClearStack(M); //每次循环都要把栈M清空
CreateMaze(), PrintMaze();
cout << "迷宫生成成功,开始寻找出口\n";
InputBeginAndEnd(); //输入起点和终点
FindWay(L); //非递归寻找出口
Trans(L, M); //利用辅助栈M将栈L倒置
cout << "\n从入口到出口路径为:\n";
PrintStack(M); //打印路径
system("pause");
}
return 0;
}
- 运行结果
根据提示输入迷宫大小,这里输入一个 12 × 12 12×12 12×12的迷宫
输入入口与出口坐标:入口为 ( 2 , 5 ) (2,5) (2,5),出口 ( 1 , 6 ) (1,6) (1,6)
寻找到出口的路径
另外给出两组不同的数据进行测试
迷宫大小 15 ∗ 15 15*15 15∗15,入口为 ( 6 , 11 ) (6,11) (6,11),出口为 ( 13 , 13 ) (13,13) (13,13)
迷宫大小 18 ∗ 18 18*18 18∗18,入口为 ( 4 , 8 ) (4,8) (4,8),出口为 ( 12 , 10 ) (12,10) (12,10)