实验2-1 栈与迷宫求解
【实验目的】
1.熟悉C语言的上机环境VC6,掌握C语言程序设计方法与特点。
2.掌握栈的顺序存储结构的定义及C语言实现。
3.掌握栈的顺序存储结构上的各种基本操作。
4.应用栈实现迷宫通路算法。
5.迷宫求解的关键结构定义及C语言实现。
【问题说明】
一个迷宫可用n阶方阵表示,1表示能通过,0 表示不能通过。现假设老鼠从左上角[1,1]进入迷宫,编写算法,寻求一条从右下角[n,n] 出去的路径。下图是一个迷宫的示意图:
迷宫示意图
【算法基本思想】
迷宫求解是栈的一个典型应用。基本算法思想是:
(1)若当前位置“可通”,则纳入“当前路径”,并继续朝着“下一位置”探索,即切换“下一位置”为“当前位置”,如此重复直至到达出口;
(2)若当前位置“不可通过”,则应顺着“来向”退回到“前一通道块”,然后朝着除“来向”之外的其他方向继续探索;
(3)若该通道块的四周4个方块均“不可通过”,则应从“当前路径”上删除该通道块。
注:
(1)下一位置:“当前位置”四周4个方向(东南西北)上相邻的方块。
(2)假设用栈记录当前路径,则栈顶存放的是“当前路径上最后一个通道块”。
(3)纳入路径:当前位置入栈。
(4)从当前路径上删除前一通道块:出栈。
求迷宫中一条从入口到出口的路径的算法,具体请参见p51。
【实验内容】
1. 顺序栈的初始化、元素的入栈、出栈、以及栈的判空。
2. 迷宫关键操作:
a) 迷宫的初始化
b) 判断当前位置可否通过
c) 为能通过的地方留下足迹,为当前走过的有效的步骤数
d) 探索下一位置并返回下一位置的坐标
e) 曾走过但不通的地方也留下足迹,为-1
f) 关键算法:迷宫存在从入口到出口的通道,则求得一条存放在栈中,并返回ture
g) 输出迷宫
3. 测试:
不同入口和出口的迷宫问题求解路径。
【实验重点】
1、 C语言定义顺序栈
2、 会多文件组织方式编写程序
3、 顺序栈的初始化、元素的入栈、出栈、以及栈的判空操作
4、 迷宫的关键算法。
【实验步骤与代码指导】
1. 定义相关结构:
(1)定义位置类型
typedef struct{
int r; //迷宫矩阵的行
int c; //迷宫矩阵的列
}PosType;
(2)定义栈的元素类型
typedef struct{
int ord; //通道块在路径上的“序号”
PosType seat; //通道块在迷宫中的“坐标位置”
int di; //从此通道块走向下一通道块的“方向”,1-4表示东南西北
}SElemType;
(3)定义顺序/栈类型
typedef struct{
SElemType *base; //栈底指针,栈构造前和销毁后,base为NULL
SElemType *top; //栈顶指针
int stacksize; //当前已分配的存储空间,以元素为单位
}SqStack;
(4)定义迷宫类型
typedef int MazeType[MAXLEN][MAXLEN]; //MAXLEN*MAXLEN二维数组
2.建立头文件maze.h如下:
#ifndef _MAZE_H
#define _MAZE_H
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "common.h"
#define STACK_INIT_SIZE 100 //存储空间初始化分配量
#define STACK_INCREMENT 10 //存储空间分配增量
#define MAXLEN 10
typedef struct{
int r; //迷宫矩阵的行
int c; //迷宫矩阵的列
}PosType;
typedef struct{
int ord; //通道块在路径上的“序号”
PosType seat; //通道块在迷宫中的“坐标位置”
int di; //从此通道块走向下一通道块的“方向”,1-4表示东南西北
}SElemType; //栈的元素类型
typedef struct{
SElemType *base; //栈底指针,栈构造前和销毁后,base为NULL
SElemType *top; //栈顶指针
int stacksize; //当前已分配的存储空间,以元素为单位
}SqStack; //栈类型-顺序存储
typedef int MazeType[MAXLEN][MAXLEN]; //MAXLEN*MAXLEN二维数组 //迷宫类型
Status InitStack(SqStack &S); //初始化栈
Status DestroyStack(SqStack &S); //销毁栈
Status Push(SqStack &S,SElemType e); //向栈顶插入元素
Status Pop(SqStack &S,SElemType &e); //出栈
Status StackEmpty(SqStack S); //栈是否为空
Status InitMaze(MazeType &maze); //初始化迷宫
Status Pass(MazeType &maze,PosType curpos); //判断当前位置可否通过
Status FootPrint(MazeType &maze,PosType curpos,int curstep); //留下足迹,足迹为步骤数
PosType NextPos(PosType curpos,int i); //探索下一位置并返回下一位置的坐标
Status MarkPrint(MazeType &maze,PosType curpos); //曾走过但不通留下标记-1
//迷宫maze存在从入口start到end的通道则求得一条存放在栈中
Status MazePath(MazeType &maze,PosType start,PosType end);
void PrintMaze(MazeType &maze); //输出迷宫
#endif
3. 再建立一个头文件common.h如下:
#ifndef _COMMON_H
#define _COMMON_H
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
#endif
4. 将实现放在Maze.cpp里,文件内容如下:
#include <time.h>
#include "maze.h"
Status InitStack(SqStack &S){
S.base=(SElemType *)malloc(STACK_INIT_SIZE*sizeof(SElemType));
if(!S.base) exit(OVERFLOW);
S.top=S.base;
S.stacksize=STACK_INIT_SIZE;
return OK;
}
Status DestroyStack(SqStack &S){
if (S.base!=NULL){
free(S.base);
S.stacksize=0;
printf("顺序栈已经销毁\n");
return OK;
}
else return FALSE;
}
Status Push(SqStack &S,SElemType e){
if (S.top-S.base>=S.stacksize ){
S.base=(SElemType *) realloc(S.base,(S.stacksize+STACK_INCREMENT)*sizeof(SElemType));
if(!S.base) exit(OVERFLOW);
S.top=S.base+S.stacksize;
S.stacksize+=STACK_INCREMENT;
}
*S.top++=e;
return OK;
}
Status Pop(SqStack &S,SElemType &e){
if(S.top==S.base) return ERROR;
e=*--S.top;
return OK;
}
Status StackEmpty(SqStack S){
if (S.base==S.top) return TRUE;
else return FALSE;
}
//定义墙值为0,可通过路径为1,不可通过路径为-1
Status InitMaze(MazeType &maze){
int i,j;
for(i=0;i<MAXLEN;i++){ //迷宫行外墙
maze[0][i]=0;
maze[MAXLEN-1][i]=0;
}
for(i=0;i<MAXLEN;i++){ //迷宫列外墙
maze[i][0]=0;
maze[i][MAXLEN-1]=0;
}
srand((unsigned)time(NULL)); //随机种子
for(i=1;i<MAXLEN-1;i++)
for(j=1;j<MAXLEN-1;j++)
maze[i][j]=1; //初始化迷宫
for(i=1;i<MAXLEN-1;i++){
for(j=1;j<MAXLEN-1;j++){
int n=rand()%29; //利用随机函数随机生成n值
if(n<MAXLEN-1) maze[i][j]=0; //随机设置通道块上的不通的路块
}
}
maze[1][1]=1;
maze[MAXLEN-2][MAXLEN-2]=1;
return OK;
}
void PrintMaze(MazeType &maze){
int i,j;
printf("用递增的数字代表迷宫的从入口到出口的一条路径\n");
printf("用0代表墙和不通的地方用\n用-1代表曾走过的通道块但不通\n");
printf("用1代表可以通过的通道块\n");
printf(" ");
for(i=0;i<MAXLEN;i++) //打印列数名
printf("%4d",i);
printf("\n\n");
for(i=0;i<MAXLEN;i++){
printf("%2d",i); //打印行名
for(j=0;j<MAXLEN;j++)
printf("%4d",maze[i][j]); //输出迷宫//当前位置的标记
printf("\n\n");
}
}
Status Pass(MazeType &maze,PosType curpos){
if(maze[curpos.r][curpos.c]==1) //可通
return TRUE;
else
return FALSE;
}
Status FootPrint(MazeType &maze,PosType curpos,int curstep){
maze[curpos.r][curpos.c]=curstep; //记录可通足迹
return OK;
}
PosType NextPos(PosType curpos,int i){
PosType cpos=curpos;
switch(i){ //1.2.3.4分别表示东,南,西,北方向
case 1 : cpos.c+=1; break;
case 2 : cpos.r+=1; break;
case 3 : cpos.c-=1; break;
case 4 : cpos.r-=1; break;
default: exit(ERROR);
}
return cpos;
}
Status MarkPrint(MazeType &maze,PosType curpos){
maze[curpos.r][curpos.c]=-1; //-1表示曾走过但不通
return OK;
}
Status MazePath(MazeType &maze,PosType start,PosType end){
//若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE
SqStack S; InitStack(S); //初始化栈
PosType curpos=start; //设置"当前位置"为"入口位置"
int curstep=1; //探索第一步
SElemType e;
do{
if(Pass(maze,curpos)){ //当前位置可以通过,即未曾走过的通道块
FootPrint(maze,curpos,curstep); //留下足迹
e.ord=curstep; e.seat=curpos; e.di=1;
Push(S,e); //加入路径
if(curpos.r==end.r && curpos.c==end.c) return TRUE; //到达出口
curpos=NextPos(curpos,1); //下一位置是当前位置的东邻
curstep++; //探索下一步
}
else{ //当前位置不通
if(!StackEmpty(S)){
Pop(S,e);
curstep--;
while(e.di==4 && !StackEmpty(S)){
MarkPrint(maze,e.seat); //留下不能通过的标记,并退一步
Pop(S,e);
curstep--;
}//while
if(e.di<4){
e.di++; //换下一个方向探索
Push(S,e);
curstep++;
curpos=NextPos(e.seat,e.di); //设定当前位置是该新方向上的相邻
}
}
}
}while(!StackEmpty(S));
return FALSE;
}
5. 主函数:
#include <stdio.h>
#include "maze.h"
void main(){
MazeType maze;
PosType start,end;
InitMaze(maze); //初始化并创建迷宫
start.r=1; start.c=1; //迷宫入口坐标
end.c=MAXLEN-2;end.r=MAXLEN-2; //迷宫出口坐标
PrintMaze(maze);
if(MazePath(maze,start,end)){
printf("存在从入口到出口的迷宫路径!\n");
PrintMaze(maze); //打印路径**/
}else printf("迷宫路径不存在!\n");
}
【实验总结】
【实验心得】