OUC_数据结构与算法_第三章实验:栈实现迷宫求解问题

本文详细描述了一个C语言编程实验,要求学生实现栈结构来解决迷宫问题,包括编写可通、足迹打印、下一个位置等函数,并分析不同探索策略下的效率。实验还涉及数据结构MazeType的定义和操作,以及对迷宫求解路径长度与尝试位置数的效率评估。
摘要由CSDN通过智能技术生成

实验要求:(满分10分)

1. (2分)基于教材和课件讲解内容,利用自己实现的栈结构完成可运行的迷宫求解程序

2. (2分)实现教材或课件中未给出的“可通”函数、“足迹打印”函数、“下一位置”函数、“打印不能通过的位置”函数等功能函数

3. (2分)实现MazeType数据类型,及可能会用到的数据对象(如,入口、出口、位置属性是墙或通路)、数据关系(如,位置之间的相邻关系)、基本操作(如,返回某个坐标位置是墙还是通路)

4. (2分)测试有通路和没通路等不同结果的输入迷宫

5. (2分)尝试进一步完善迷宫求解程序,使得从入口到出口有多条简单路径的迷宫的所有路径都能求解出来,或者从多条可行的路径中给出最短路径。

拓展要求:(附加3分)

1.(2分) 通过实验结果对比“入口-出口相对方向”和“探索方向的优先顺序”一致或不一致时,迷宫求解程序的运行效率。例如,当出口在入口的右下方向时,探索优先顺序是右下左上,或者上左下右时,程序运行“时间/效率/试过的位置数”是不一样的

2.(1分)分析“可通”函数原理,解释为什么迷宫求解程序得到的路径不会有环

实验时长:

3周完成

C语言代码如下:

#include <stdio.h>
#include <malloc.h>
#include <time.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+2]={//+2是为了构造围墙 
	{1,1,1,1,1,1,1,1,1,1},
	{1,0,0,1,0,0,0,1,0,1},
	{1,0,0,1,0,0,0,1,0,1},
	{1,0,0,0,0,1,1,0,0,1}, 
	{1,0,1,1,1,0,0,0,0,1},				//2(0->1)即可不通 
	{1,0,0,0,1,0,0,0,0,1},
	{1,0,1,0,0,0,1,0,0,1},
	{1,0,1,1,1,0,1,1,0,1},
	{1,1,0,0,0,0,0,0,0,1},
	{1,1,1,1,1,1,1,1,1,1}
};
int i,j,di,i1,j1,t=3,count=0;
int length[2][4] = {MaxSize};
int number1 = 0,number2 = 0; //统计位置数 
//迷宫栈基本运算
typedef struct{
	int i;				//当前方块的行号
	int j;				//当前方块的列号
	int di;				//di是下一可走相邻方位的方位号
} Box;

typedef struct{
	Box data[MaxSize];	//存放方块
    int top;			//栈顶指针
} StType;				//定义栈类型

//实验要求 
typedef struct{
	int xi,yi;//入口坐标 
	int xe,ye;//出口坐标 
	int MT[M+2][N+2];//位置属性是墙还是通路
	void MTInit(); //默认初始化方案
	void rela();//位置之间的相邻关系
	void info();//返回某个坐标是墙或者通路
} MazeType; 

void MazeType::MTInit(){//默认初始化方案
	for(int i=0;i<M+2;i++){
		for(int j=0;j<N+2;j++){
			this->MT[i][j] = mg[i][j];
		}
	}
} 

void MazeType::info(){	//返回某个坐标是墙或通路
	int x,y; 
	printf("请输入坐标:");
	scanf("%d %d",&x,&y);
	if(x>=M+2 || y>=N+2){//越界 
		printf("坐标越界!\n");
	}else if(this->MT[x][y]==0){//通路 
		printf("通路!\n");
	}else{//墙
		printf("墙!\n");
	}
}

void MazeType::rela(){ //位置之间的相邻关系 
	int x1,y1,x2,y2;
	printf("请依次输入两点坐标,以判断其相邻关系:");
	scanf("%d %d",&x1,&y1);
	scanf("%d %d",&x2,&y2);

	int direct = 4;
	if(x1>=M+2 || y1>=N+2 || x2>=M+2 || y2>=N+2){//越界 
		direct = -1;
	}else if(x1==x2){//0123上右下左 
		if(y1==y2+1){
			direct = 2;//以1为参照 
		}else if(y1==y2-1){
			direct = 0;
		}else{
			direct = 4;//不相邻 
		}
	}else if(y1==y2){
		if(x1==x2+1){
			direct = 3;//以1为参照 
		}else if(x1==x2-1){
			direct = 1;
		}else{
			direct = 4;//不相邻 
		}
	}
	switch(direct){
		case -1:printf("输入的坐标越界!\n",x2,y2,x1,y1);break;//越界 
		case 0:printf("(%d,%d)是(%d,%d)的上邻。\n",x2,y2,x1,y1);break;//往上 
		case 1:printf("(%d,%d)是(%d,%d)的右邻。\n",x2,y2,x1,y1);break;//往右 
		case 2:printf("(%d,%d)是(%d,%d)的下邻。\n",x2,y2,x1,y1);break;//往下 
		case 3:printf("(%d,%d)是(%d,%d)的左邻。\n",x2,y2,x1,y1);break;//往左
		default:printf("两点不相邻!\n");//不相邻 
	}
} 

//栈结构 
void InitStack(StType *&s){		//初始化栈
	s=(StType *)malloc(sizeof(StType));
	s->top=-1;
}

void DestroyStack(StType *&s){	//销毁栈
	free(s);
}

bool StackEmpty(StType *s){		//判断栈是否为空
	return(s->top==-1);
}

bool Push(StType *&s,Box e){	//入栈元素e
	if (s->top==MaxSize-1){//栈溢出则报错 
		return false;
	}
	s->top++;
	s->data[s->top]=e;
	return true;
}

bool Pop(StType *&s,Box &e){	//出栈元素e
	if (s->top==-1){//栈为空则报错 
		return false;
	}
	e=s->data[s->top];
	s->top--;
	return true;
}

bool GetTop(StType *s,Box &e){	//取栈顶元素e
	if (s->top==-1){//栈为空则报错 
		return false;
	}	
	e=s->data[s->top];
	return true;
}

void displaymg(int flag){
	if(flag==0){
		printf("迷宫MazeType信息如下:\n");
	}else if(flag==1){
		printf("路径按字母顺序展示:\n");
	}
	printf(" ");
	for(int i=0;i<10;i++){
		printf("%d",i);
	} 
	printf("\n");
	for(int i=0;i<10;i++){
		printf("%d",i);
		for(int j=0;j<10;j++){
			if(mg[i][j]==1){//墙 
				printf("*");
			}else if(mg[i][j]==0){
				printf(" ");
			}else{//路径 
				printf("%c",'a'+(mg[i][j]-2)%26);
			}
		}
		printf("\n"); 
	}
} 

//实验要求 
bool Pass(int flag){//“可通”函数
	bool find=false;
	while (di<4 && !find){				//找相邻可走方块(i1,j1)	
		di++; 
		switch(flag){//通过flag进行顺序选择
			case 0:switch(di){//1230右下左上顺时针 
						case 1:i1=i-1; j1=j;   break;//往上 
						case 2:i1=i;   j1=j+1; break;//往右 
						case 3:i1=i+1; j1=j;   break;//往下 
						case 0:i1=i;   j1=j-1; break;//往左 
					};break;
			case 1:switch(di){//1032上左下右逆时针 
						case 1:i1=i-1; j1=j;   break;//往上 
						case 0:i1=i;   j1=j+1; break;//往右 
						case 3:i1=i+1; j1=j;   break;//往下 
						case 2:i1=i;   j1=j-1; break;//往左 
					};break;
//			case 0:switch(di){//0123上左下右顺时针 
//						case 0:i1=i-1; j1=j;   break;//往上 
//						case 1:i1=i;   j1=j+1; break;//往右 
//						case 2:i1=i+1; j1=j;   break;//往下 
//						case 3:i1=i;   j1=j-1; break;//往左 
//					};break;
//			case 1:switch(di){//1230上右下左顺时针 
//						case 1:i1=i-1; j1=j;   break;//往上 
//						case 2:i1=i;   j1=j+1; break;//往右 
//						case 3:i1=i+1; j1=j;   break;//往下 
//						case 0:i1=i;   j1=j-1; break;//往左 
//					};break;
//			case 2:switch(di){//2301上右下左顺时针 
//						case 2:i1=i-1; j1=j;   break;//往上 
//						case 3:i1=i;   j1=j+1; break;//往右 
//						case 0:i1=i+1; j1=j;   break;//往下 
//						case 1:i1=i;   j1=j-1; break;//往左 
//					};break;
//			case 3:switch(di){//3012上右下左顺时针 
//						case 3:i1=i-1; j1=j;   break;//往上 
//						case 0:i1=i;   j1=j+1; break;//往右 
//						case 1:i1=i+1; j1=j;   break;//往下 
//						case 2:i1=i;   j1=j-1; break;//往左 
//					};break;
		}
		if (mg[i1][j1]==0){	//找到一个相邻可走方块,设置find为真
			find=true;
		}
	}
	return find;
} 

int FootPrint(Box path[],int k){//“足迹打印”函数
	int c=0;
	while (k>=1){
		k--;
		c++;
		printf("(%d,%d)",path[k].i,path[k].j);
		if(k>=1){
			printf("->");
		}
		if (c%5==0){				//每输出每5个方块后换一行
			printf("\n");
		}
	}
	printf("\n");
	return c;//c是路径长度 
} 

void NextPos(StType *st,Box e){//“下一位置”函数 
	st->data[st->top].di=di;		//修改原栈顶元素的di值
	e.i=i1; e.j=j1; e.di=-1;
	//printf("下一位置为(%d,%d)\n",i1,j1);
	Push(st,e);						//相邻可走方块e进栈
}

void MarkPrint(int i,int j){//“打印不能通过的位置”函数 
	//printf("(%d,%d)不能通过\n",i,j);
}

//迷宫求解 
bool mgpath(int flag,int xi,int yi,int xe,int ye){	//求解路径为:(xi,yi)->(xe,ye)
	Box path[MaxSize], e;
	StType *st;								//定义栈st
	InitStack(st);							//初始化栈顶指针
	e.i=xi; e.j=yi;	e.di=-1;				//设置e为入口
	Push(st,e);								//方块e进栈
	mg[xi][yi]=2;							//入口的迷宫值置为2避免重复走到该方块
	while (!StackEmpty(st)){				//栈不空时循环
		GetTop(st,e);						//取栈顶方块e
		i=e.i; j=e.j; di=e.di;
		if (i==xe && j==ye){				//找到了出口,输出该路径
			//printf("--------------------\n第%d条迷宫路径如下:\n",count+1);
			int k=0;
			while (!StackEmpty(st)){
				Pop(st,e);					//出栈方块e
				path[k++]=e;				//将e添加到path数组中
			}	
			length[0][count] = count+1; 		//第一行存储序号 
			length[1][count++] = FootPrint(path,k);	//第二行存储长度
			printf("长度为:%d\n",length[1][count-1]);	
			DestroyStack(st);				//销毁栈
			return true;					//输出一条迷宫路径后返回true
		}
		if (Pass(flag)){						//Pass()找到了一个相邻可走方块(i1,j1)
			//printf("(%d,%d)可通\n",i1,j1);
			NextPos(st,e);
			mg[i1][j1]=t++;					//(i1,j1)的迷宫值置为t(小写字母)避免重复走到该方块
			if(flag==0){
				number1++;
			}else{
				number2++;
			}
			//displaymg(1);
		}else{								//没有路径可走,则退栈
			MarkPrint(e.i,e.j); 
			Pop(st,e);						//将栈顶方块退栈
			mg[e.i][e.j]=0;					//让退栈方块的位置变为其他路径可走方块
			t--;
			//displaymg(1);
		}
	}
	DestroyStack(st);						//销毁栈
	return false;							//表示没有可走路径,返回false
}
//最短路径 
void BestPath(){
	//对length按长度进行冒泡排序
	for(int i=0;i<3;i++){
		for(int j=0;j<3-i;j++){
			if(length[1][j]>length[1][j+1]){
				int c1 = length[1][j];
				length[1][j] = length[1][j+1];
				length[1][j+1] = c1;
				
				int c2 = length[0][j];
				length[0][j] = length[0][j+1];
				length[0][j+1] = c2;
			}
		}
	}
	printf("\n最短路径为:第%d条	长度为:%d",length[0][0],length[1][0]);	
} 

int main(){
	int startTime1,startTime2,endTime1,endTime2;
	MazeType mt;
	mt.MTInit();//储存mg信息 
	displaymg(0);//展示迷宫 
	int xi,yi,xe,ye;
	printf("请输入入口坐标(xi yi):");
	scanf("%d %d",&xi,&yi);
	printf("请输入出口坐标(xe ye):");
	scanf("%d %d",&xe,&ye);
	printf("满足出口在入口的右下方向。\n");
	for(int flag=0;flag<2;flag++){//4;flag++){//顺时针探索
		if(flag==0){
			startTime1 = clock();
			//printf("startTime1 = %.2f",startTime1);
			printf("\n“右下左上”探索开始!\n");
		}else{
			startTime2 = clock();
			//printf("startTime2 = %.2f",startTime2);
			printf("\n“上左下右”探索开始!\n");
		}
		if(mgpath(flag,xi,yi,xe,ye)){
			displaymg(1);//展示路径
		}else{
			printf("无可实现的路径!");
		}
		
		for(int i=0;i<M+2;i++){//reset 
			for(int j=0;j<N+2;j++){
				mg[i][j] = mt.MT[i][j];
			}
		}
		
		t=3; 
		if(flag==0){
			endTime1 = clock();
			//printf("endTime1 = %.2f",endTime1);
			float n1 = (float)length[1][0]/(float)number1*100;
			printf("“右下左上”探索完毕,耗时%d秒,效率为%.1f%%,试过的位置数为%d个\n",endTime1-startTime1,n1,number1);
		}else{
			endTime2 = clock();
			//printf("endTime2 = %.2f",endTime2);
			float n2 = (float)length[1][1]/(float)number2*100;
			printf("“上左下右”探索完毕,耗时%d秒,效率为%.1f%%,试过的位置数为%d个\n",endTime2-startTime2,n2,number2);
		}
		for(int i=0;i<M+2;i++){//清除路径进入下一轮探索 
			for(int j=0;j<N+2;j++){
				mg[i][j] = mt.MT[i][j];
			}
		} 
	}
	printf("(效率=最终路径长度/试过的位置数)\n"); 
	//选最短路径 
	//BestPath();
	return 0;
}

记录一下曾经的努力,欢迎大家围观与讨论!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值