数据结构实验四

实验题目:栈和队列的综合应用

1.迷宫问题。假设迷宫由m行n列构成,有一个入口和一个出口,入口坐标为(1,1),出口坐标为(m,n),试设计并验证以下算法:找出一条从入口通往出口的路径,或报告一个“无法通过”的信息。

要求

(1) 用C语言实现顺序存储结构上队列的基本操作,然后利用该队列的基本操作找出迷宫的一条最短路径。
(2) 设计一个二维数组MAZE[m+2][n+2]表示迷宫,数组元素为0表示该位置可以通过,数组元素为1表示该位置不可以通行。MAZE[1][1]、MAZE[m][n]分别为迷宫的入口和出口。
(3) 输入迷宫的大小m行和n列,动态生成二维数组;由随机数产生0或1,建立迷宫,注意m*n的迷宫需要进行扩展,扩展部分的元素设置为1,相当于在迷宫周围布上一圈不准通过的墙。
(4) 要求输出模拟迷宫的二维数组;若存在最短路经,则由出口回溯到入口(出队列并利用栈实现),再打印从入口到出口的这条路径,例如(1,1),……,(i,j),……,(m,n);若没有路径,则打印“No path!”。
(5) 为避免出现原地踏步的情况为了标志已经通过的位置,采用一个标志数组MARK[m+2][n+2],初值均为0,在寻找路径的过程中,若通过了位置(i,j),则将MARK[i][j]置为1。
(6) 为了记录查找过程中到达位置(i,j)及首次到达(i,j)的前一位置(i_pre,j_pre),需要记住前一位置(i_pre,j_pre)在队列中的序号pre,即队列中数据元素应该是一个三元组(i,j,pre)。
(7) 搜索过程简单描述如下:将入口MAZE[1][1]作为第一个出发点,依次在八个方向上搜索可通行的位置,将可通行位置(i,j,pre)入队,形成第一层新的出发点,然后依次出队,即对第一层中各个位置分别搜索它所在八个方向上的可通行位置,形成第二层新的出发点,…,如此进行下去,直至达到出口MAZE[m][n]或者迷宫所有位置都搜索完毕为止。

审题
  1. 用队列解迷宫问题,迷宫要求动态生成,队列中数据元素应该是一个三元组
  2. 在寻找路径的过程中,若通过了位置(i,j),则将MARK[i][j]置为1,二维数组MAZE[m+2][n+2]表示迷宫,数组元素为0表示该位置可以通过,数组元素为1表示该位置不可以通行
  3. 由出口回溯到入口(出队列并利用栈实现),再打印从入口到出口的这条路径
抽象数据定义
typedef struct {
    int x;//点的坐标
    int y;
    int pre; //记录该结点的父结点(来源)
}position;

typedef struct my_queue {//队列
    position data[1000];
    int front;
    int rear;
}Queue;

typedef struct{//栈
	position data[1000];
	int top;
}Stack;
算法思路
如何随机生成迷宫,搜索路径和如何打印路径
  1. 随机生成迷宫的关键是如何随机生成范围内的数字:
    大概就是用一个根据系统时间生成的种子随机生成数,具体的细节,比如为什么需要一个种子,和获得时间为什么这样写,笔者也不知道。
//随机函数,生成minnum到maxnum的随机整数 
int random_index(int minnum,int maxnum) {
    struct timespec time1 = { 0, 0 };
    clock_gettime(CLOCK_REALTIME, &time1);//获得毫秒级的时间
    srand(time1.tv_nsec);//根据系统时间生成种子
    int choice = maxnum - minnum + 1;
    int a = rand() % choice + minnum;//生成minnum到maxnum的随机整数
    return a;
}
  1. 搜索路径,建议食用视频,b站麦克老师讲算法,我这里也大概讲一下吧。
    搜索算法:起点进队列,然后是一个大的队列非空循环,队首元素出队,如果队首元素是出口,那么找到了,循环结束,不然就看看这个元素周围可不可以走,可以走的全部进队(小循环),队列非空就继续队首元素出队,直到队列为空或是找到出口。
push(qu,e);//起点进队列
	while(!EmptyQueue(qu)){//大的队列非空循环
		pop(qu,e);//队首元素出队
		i=e.x,j=e.y,pre=e.pre;//如果队首元素是出口,那么找到了,循环结束
		if(i==m&&j==n){
			print(qu,qu->front);
			return 1;
		}
		int d=-1;
		int in,jn;
		while(d<8){//上,上右,右,下右,下,下左,左,上左
			d++;
			switch(d){
				case 0:{in=i-1,jn=j;break;}
				case 1:{in=i-1,jn=j+1;break;}
				case 2:{in=i,jn=j+1;break;}
				case 3:{in=i+1,jn=j+1;break;}
				case 4:{in=i+1,jn=j;break;}
				case 5:{in=i+1,jn=j-1;break;}
				case 6:{in=i,jn=j-1;break;}
				case 7:{in=i-1,jn=j-1;break;}
			}
			if(MAZE[in][jn]==0){//看看这个元素周围可不可以走,可以走的全部进队(小循环)
				e.x=in,e.y=jn,e.pre=qu->front;
				push(qu,e);
				MAZE[in][jn]=-1;
			}
		}
	}
  1. 打印路径,也建议食用视频,b站麦克老师讲算法。
    打印算法:
    搜索时,pre记录了上个点在队列的位置。
void print(Queue *&qu,int k)//k为q->front
{
	int j;//因为进队出队没有空间操作,只是改变了头尾指针的值
	Stack s;
	s.top=-1;
	while(k!=-1){//所以可以通过当前的qu->front回溯 
		j=k;//因为pre记录了该节点是从那个节点过来的 
		k=qu->data[k].pre;//所以可以不断通过pre索引到起点 
		s.data[++s.top]=qu->data[j]; //进栈
	}
	int ns=0;//打印路径节点坐标,ns用来换行 
	while(s.top!=-1){//出栈
		ns++;
		printf("(%d,%d)",s.data[s.top].x,s.data[s.top].y);
		s.top--;
		if(ns%5==0)
			printf("\n");
		else
			printf("\t");
		
	}
	printf("\n");
}
详细代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>

#define N 100

int MAZE[100][100];
//int Direction[8][2]={{0,1},{1,1},{0,-1},{-1,-1},{1,1},{0,-1},{-1,-1},{0,1}};
typedef struct {
    int x;
    int y;
    int pre; //记录该结点的父结点(来源)
}position;

typedef struct my_queue {
    position data[1000];
    int front;
    int rear;
}Queue;

typedef struct{
	position data[1000];
	int top;
}Stack;

//顺序存储结构上队列的基本操作
void InitQue(Queue *&q)//初始化队列 
{
	q=(Queue *)malloc(sizeof(Queue));
	q->front=q->rear=-1;
}

int EmptyQueue(Queue *&q){//判空 
	return (q->front==q->rear);
}

int push(Queue *&q,position &e){//进队 
	if(q->rear>999)
		return 0;
	e.pre=q->front;
	q->rear++;
	q->data[q->rear]=e;
	return 1;
}

int pop(Queue *&q,position &e){//出队 
	if(EmptyQueue(q))
		return 0;
	q->front++;
	e=q->data[q->front];
	return 1;
}
//打印路径 
void print(Queue *&qu,int k)
{
	int j;//因为进队出队没有空间操作,只是改变了头尾指针的值
	Stack s;
	s.top=-1;
	while(k!=-1){//所以可以通过当前的qu->front回溯 
		j=k;//因为pre记录了该节点是从那个节点过来的 
		k=qu->data[k].pre;//所以可以不断通过pre索引到起点 
		s.data[++s.top]=qu->data[j]; 
	}
	int ns=0;//打印路径节点坐标,ns用来换行 
	while(s.top!=-1){
		ns++;
		printf("(%d,%d)",s.data[s.top].x,s.data[s.top].y);
		s.top--;
		if(ns%5==0)
			printf("\n");
		else
			printf("\t");
		
	}
	printf("\n");
}
//搜索路径 
int mgpath1(int x,int y,int m,int n)
{
	position e;
	int i,j,pre;
	Queue *qu;
	InitQue(qu);
	
	e.x=x,e.y=y,e.pre=-1;
	MAZE[x][y]=-1;
	push(qu,e);
	
	while(!EmptyQueue(qu)){
		pop(qu,e);
		i=e.x,j=e.y,pre=e.pre;
		if(i==m&&j==n){
			print(qu,qu->front);
			return 1;
		}
		int d=-1;
		int in,jn;
		while(d<8){//上,上右,右,下右,下,下左,左,上左
			d++;
			switch(d){
				case 0:{in=i-1,jn=j;break;}
				case 1:{in=i-1,jn=j+1;break;}
				case 2:{in=i,jn=j+1;break;}
				case 3:{in=i+1,jn=j+1;break;}
				case 4:{in=i+1,jn=j;break;}
				case 5:{in=i+1,jn=j-1;break;}
				case 6:{in=i,jn=j-1;break;}
				case 7:{in=i-1,jn=j-1;break;}
			}
			if(MAZE[in][jn]==0){
				e.x=in,e.y=jn,e.pre=qu->front;
				push(qu,e);
				MAZE[in][jn]=-1;
			}
		}
	}
	return 0;
}
//随机函数,生成minnum到maxnum的随机整数 
int random_index(int minnum,int maxnum) {
    struct timespec time1 = { 0, 0 };
    clock_gettime(CLOCK_REALTIME, &time1);
    srand(time1.tv_nsec);
    int choice = maxnum - minnum + 1;
    int a = rand() % choice + minnum;
    return a;
}

int main()
{
	int m,n;
	printf("请输入迷宫行数"); 
	scanf("%d",&m);
	printf("请输入迷宫列数"); 
	scanf("%d",&n);
	for(int i=0;i<m+2;i++){
		for(int j=0;j<n+2;j++){
			if(i==0||i==m+1)
				MAZE[i][j]=1;
			else if(j==0||j==n+1)
				MAZE[i][j]=1;
			else
				MAZE[i][j]=random_index(0,100)%2;
			MAZE[1][1]=0;
			MAZE[m][n]=0;
			printf("%d ",MAZE[i][j]);
			Sleep(m+n);
		}
		printf("\n");
	}
	
	if(!mgpath1(1,1,m,n))
		printf("无解");
	return 0;
}
  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值