2017年数据结构课程设计--迷宫问题

栈与迷宫问题

【问题描述】

以一个mXn的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。

【任务要求】

1) 首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出。其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。如,对于下列数据的迷宫,输出一条通路为:(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),…。

2) 编写递归形式的算法,求得迷宫中所有可能的通路。

3) 以方阵形式输出迷宫及其通路。

 

【问题分析】

走迷宫程序就是按照在某个位置,判断四周方向选择可通方向进行探索直到到达终点。

 

 

主要步骤:

(1) 对数据进行预处理

(2) 按照右手法则(优先方向)探索,找到一条路径

(3) 根据一条路径回退找到其他通路找到一条路径模拟图

《2017年数据结构课程设计--迷宫问题》

《2017年数据结构课程设计--迷宫问题》

找到一条路径就是执行一次这个过程,找到所有路径就是重复这个过程,直到在每一个分岔口都试过了所有的方向。

在寻找所有路径的过程中,有一些路径在第二次经过的时候不需要再探索。

实践过程

在这次试验过程中,主要知识点是利用栈的特性(FIFO)实现迷宫算法。

结合本题特性,加上和老师讨论。主要分三步骤:1、对地图进行预处理,堵住一些死路(三边都被堵住)。2、沿某个方向找到一条路径。3、根据步骤二得到的路径退到分岔路口,继续搜索。

《2017年数据结构课程设计--迷宫问题》

预处理:

1. 扫描地图,利用九宫格的想法将并行的通路堵住.以E点为中心,判断E的来路与去路选择将要堵住的其他路径。(给出两种情况堵住其他出路)

《2017年数据结构课程设计--迷宫问题》

1. 回溯法,堵住思路(规定一个结点的三边都是1则说明是死路将此结点堵住)

深度优先搜素:

找到一条路径,将结点入栈,并做好标记,如果是分岔路口将cross_road=1

找到所有路径:

根据栈中的结点出栈操作,换个方向改变(x,y)坐标就行搜索。

思考过程及所遇到的问题说明:

在最初的过程中,对于题目的不熟悉。看到网上论坛写的都是一些递归查找地图的路径,与老师提供思路不一致。没有利用到搜索一条路径的信息导致资源浪费。最后同老师交流,自己在过程中所遇到问题和改正方法陈述如下:

程序采用面向对象的思路:将整个程序分为三个部分栈类、地图类、迷宫类。其中地图类中的是每个结点具有的属性。

最初的设计思想是将up\down\right\left\cross_road\value设置为地图中每个结点的属性原因在于每个结点都有上下左右四个方向要探索加上判断是否是岔路口和每个结点的值(0/1)。在程序编写过程中发现up\down\right\left四个变量的多余性(可以根据左边运算进行判定),便将其删除发现每个结点的占据的空间从24个字节节省到8个字节。

其次和老师交流的是如何处理并行路径的方案。和之前自己确定下来的四宫格方案相比,老师提出来的九宫格方案能够较好解决对称问题,在此过程中我想到过围棋中的无气之子的判别方案但是在应用过程中有很大不足。最终想到每行每列只用最多只有八种方案,既可以判断从哪一点进来从那个点出去来堵住起来重复路径。

程序实现
预处理
void maze::process(int x,int y){
	if(map[x][y].value)	return;
	else{
		//A=0 B=0的情况 从B点进入 
		if(!map[x-1][y-1].value && !map[x-1][y].value ){
			if(!map[x-1][y+1].value)		map[x][y].value=1;	//A->B->C将E堵住 
			else if(!map[x][y+1].value)		map[x+1][y].value=1;	//A->B->E->F
		} 
		//如果A||B有一个为1,并且D=0的情况 从D点进入  
		else if((map[x-1][y-1].value || map[x-1][y].value) && !map[x][y-1].value)	{
				if(!map[x][y+1].value)	map[x+1][y].value=1; 	//D->E->F	 
		}
		//A=1 D=1 G=0 
		else if(map[x-1][y-1].value && map[x][y-1].value && !map[x+1][y-1].value){
			if(!map[x+1][y].value && !map[x+1][y+1].value)	map[x][y].value=1; //G->H->I 
		}
		else if(map[x-1][y-1].value && map[x][y-1].value && map[x+1][y-1].value) {
			if(!map[x-1][y+1].value)	map[x][y].value=1;
			else if(!map[x][y+1].value)	map[x+1][y].value=1;
		}
	}
}

int maze::judge(int x,int y)
{
	//向下
	if(map[x-1][y].value==1 && map[x][y-1].value==1 && map[x][y+1].value==1 && !map[x+1][y].value)		return 1;
	//向右
	if(map[x-1][y].value==1 && map[x][y-1].value==1 && !map[x][y+1].value && map[x+1][y].value==1)		return 0;
	//向左
	if( map[x-1][y].value==1 && !map[x][y-1].value && map[x][y+1].value==1 && map[x+1][y].value==1)		return 2;
	//向上
	if(!map[x-1][y].value && map[x][y-1].value==1 && map[x][y+1].value==1 && map[x+1][y].value==1)		return 3;
	return 4;
}
void maze::traverse(Map map[][Col+2])
{
	int x=0,y=1,number=0,i,j;
	for(i=1;i<m-1;i++)
	{
		x=i;
		for(j=1;j<n-1;j++)
		{
			y=j;
			if(map[i][j].value==0)
			{	
				number=judge(i,j);
				switch(number)
				{	
				case 0:map[i][j].value=1;continue;
				case 1:map[i][j].value=1;i++;j--;continue;
				case 2:map[i][j].value=1;j=j-2;continue;
				case 3:map[i][j].value=1;i--;j--;continue;
				case 4:i=x;j=y;continue;
				}
			}
			else
			{
				i=x;j=y;
			}
		}
	}
}
说明:先对地图进行堵住并行的通路在堵住死路;
深度优先搜索找到一条路径
int maze::seekonepath(int x,int y,int d,linkstack &stack)
{
	int row,col;
	while(!stack.isempty()){
		x=stack.top->x;		y=stack.top->y;	d=stack.top->d+1;
		stack.pop(stack);
		while(d<4){
			row=x+move[d][0];	col=y+move[d][1];
			if(map[row][col].value == 0){
				if(x==0 && y==1 && d==3)	return 0;
				stack.push(x,y,d);
				if(path_judge(x,y))						//判断岔路口
				{
					map[x][y].cross_road=1;
				}
				else	map[x][y].cross_road=0;
				x=row; y=col; 
				if((x==m-2) && (y==n-1)){
					stack.push(x,y,d);
					sum++;
					return 1;
				}
				else d=0;
			}
			else d++;
		}
	}
	return 0;
}
说明:没经过一个结点就判断他是否是岔路口如果是岔路口就标记下来。
非递归找到所有路径
//递归找出所有的通路
void maze::recursion_findpath(linkstack stack)
{
	cout<<"进入寻找多条路径的函数中"<<endl;
	while(!stack.isempty()){
			if (map[stack.top->x][stack.top->y].cross_road == 1){
				map[m-2][n-1].value=0;
				while(!seekonepath(stack.top->x,stack.top->y,stack.top->d,stack)){	
					break;
				}
		}
		else{
			stack.pop(stack);
		} 
	}
	cout<<"递归查找完毕!"<<endl;
}
程序源代码
/**
优化的部分:1.改善了一些关于判断岔路口的函数
			2.重写了seekonepath()函数 
			3.5/11 添加一个预处理函数process(int m,int n) 用来处理全0情况 堵住并行通路;
			4.5/20 重新写process()函数 达到的效果利用九宫格的形式实现堵住并列通路的问题;
@author: yanzz
@qq:
@完成时间:2017/5/20 
@完成课题:利用栈的原理找出迷宫的所有解
@完成过程中的收获: 
*/
#include<iostream>
#include<iomanip>
#include<fstream>
#include<time.h>
using namespace std;
int move[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
ifstream file;
//结点类
class linkstacknode
{
friend	class linkstack;
friend  class maze;
public:
	linkstacknode(int newx,int newy,int newd,linkstacknode *nextp);
private:
	int x,y,d;
	linkstacknode *next;
};
//栈类
class linkstack
{
	friend class map;
public:
	linkstack();
	void push(int x,int y,int d);
	void pop(linkstack stack);
	bool isempty(); 
	void print_stack();
	linkstacknode *top;
private:
	int stack_length;
};
//地图类
class Map
{	
public :
//	int up,down,left,right;
	int value;
	int cross_road;
};
const int Row=10000;
const int Col=10000;
Map map[Row+2][Col+2];
//Map **map=new Map* [Row+2];
//迷宫类
class maze
{
	friend class linkstack;
public:
	void create_map();
	void show(int m,int n);
	int judge(int x,int y);
	void traverse(Map map[][Col+2]);
	void process(int row,int col);
	int seekonepath(int x,int y,int direction,linkstack &_stack);			
	bool path_judge(int x,int y);
	void recursion_findpath(linkstack stack);
	int m,n;
private:
	linkstack stack;
};
linkstacknode::linkstacknode(int newx,int newy,int newd,linkstacknode *nextp)
{
	x=newx;
	y=newy;
	d=newd;
	next=nextp;
}
linkstack::linkstack()
{	
	top=NULL;
	stack_length=0;
	push(0,1,-1);
}

bool linkstack::isempty()
{
	return stack_length<=0?true:false;
}

void linkstack::push(int x,int y,int d)
{
	linkstacknode *newnode=new linkstacknode(x,y,d,top);
	if(newnode)
	{
		top=newnode;
		map[top->x][top->y].value=-1;
	//	print_stack();
		stack_length++;
	}
}

void linkstack::pop(linkstack stack)					
{
	linkstacknode *usednode;
	if(!isempty())
	{
		usednode=stack.top;
		top=top->next;
		map[usednode->x][usednode->y].value=0;
		delete usednode;
		stack_length--;
	}
}

void linkstack::print_stack()
{
	linkstacknode *p=top;
	cout<<"栈中内容:"<<endl;
	while(p)
	{
		cout<<p->x<<setw(4)<<p->y<<setw(4)<<p->d<<endl;
		p=p->next;
	}
	cout<<"**********************************"<<endl;
}
//建立地图
void maze::create_map()
{	
	int i,j;
	file.open("test1.txt",ios::binary);
	file>>m>>n;
	for(i=0;i<m;i++)
	{
		for(j=0;j<n;j++)
		{
			if(i==0||j==0||i==m-1||j==n-1)
			{
				map[i][j].value=1;
			}
			else
			{
				file>>map[i][j].value;			
			}
		}
	}
	map[0][1].value=0;
	map[m-2][n-1].value=0;
	traverse(map);
	show(m,n);
	for(i = 1;i<m-1;i++){
		for(j = 1; j<n-1;j++){
			process(i,j);
		}
	}
	show(m,n);
	getchar();
	traverse(map);
	cout<<"对地图的处理完成!"<<endl; 
	system("pause");
	if(seekonepath(0,1,-1,stack)){
	
		cout<<"分叉路口坐标:"<<endl;
		for(i=0;i<m;i++)
		{
			for(j=0;j<n;j++)
				if(map[i][j].cross_road==1)
				{
					cout<<i<<setw(4)<<j<<setw(10);
				}
		}
		cout<<endl<<"一条路寻找完毕,进入多条路查找:"<<endl;
		getchar();
		recursion_findpath(stack);
	}
	else{
		cout<<"没有出路"<<endl;
	}
	show(m,n);
}
void maze::process(int x,int y){
	if(map[x][y].value)	return;
	else{
		//A=0 B=0的情况 从B点进入 
		if(!map[x-1][y-1].value && !map[x-1][y].value ){
			if(!map[x-1][y+1].value)		map[x][y].value=1;	//A->B->C将E堵住 
			else if(!map[x][y+1].value)		map[x+1][y].value=1;	//A->B->E->F
		} 
		//如果A||B有一个为1,并且D=0的情况 从D点进入  
		else if((map[x-1][y-1].value || map[x-1][y].value) && !map[x][y-1].value)	{
				if(!map[x][y+1].value)	map[x+1][y].value=1; 	//D->E->F	 
		}
		//A=1 D=1 G=0 
		else if(map[x-1][y-1].value && map[x][y-1].value && !map[x+1][y-1].value){
			if(!map[x+1][y].value && !map[x+1][y+1].value)	map[x][y].value=1; //G->H->I 
		}
		else if(map[x-1][y-1].value && map[x][y-1].value && map[x+1][y-1].value) {
			if(!map[x-1][y+1].value)	map[x][y].value=1;
			else if(!map[x][y+1].value)	map[x+1][y].value=1;
		}
	}
}

int maze::judge(int x,int y)
{
	//向下
	if(map[x-1][y].value==1 && map[x][y-1].value==1 && map[x][y+1].value==1 && !map[x+1][y].value)		return 1;
	//向右
	if(map[x-1][y].value==1 && map[x][y-1].value==1 && !map[x][y+1].value && map[x+1][y].value==1)		return 0;
	//向左
	if( map[x-1][y].value==1 && !map[x][y-1].value && map[x][y+1].value==1 && map[x+1][y].value==1)		return 2;
	//向上
	if(!map[x-1][y].value && map[x][y-1].value==1 && map[x][y+1].value==1 && map[x+1][y].value==1)		return 3;
	return 4;
}
void maze::traverse(Map map[][Col+2])
{
	int x=0,y=1,number=0,i,j;
	for(i=1;i<m-1;i++)
	{
		x=i;
		for(j=1;j<n-1;j++)
		{
			y=j;
			if(map[i][j].value==0)
			{	
				number=judge(i,j);
				switch(number)
				{	
				case 0:map[i][j].value=1;continue;
				case 1:map[i][j].value=1;i++;j--;continue;
				case 2:map[i][j].value=1;j=j-2;continue;
				case 3:map[i][j].value=1;i--;j--;continue;
				case 4:i=x;j=y;continue;
				}
			}
			else
			{
				i=x;j=y;
			}
		}
	}
}
//显示地图
void maze::show(int m,int n)
{
	int x,y;
	for(x=0;x<m;x++)
	{
		for(y=0;y<n;y++)
		{
			cout<<setw(4)<<map[x][y].value;
		}
		cout<<endl;
	}
	cout<<"**********************************"<<endl;
}
//找到一条路  扫描分岔路口
bool maze::path_judge(int x,int y)
{
	if(x>0&&x<m-2){
		int n=map[x-1][y].value+map[x+1][y].value+map[x][y+1].value+map[x][y-1].value;
		if(n<=0) 	return true;
	}
	else if(x==0 || x==m-1)	return false;
	else if(x==m-2 && map[x-1][y].value==0 && map[x][y+1].value==0)	return true;
	return false;
}
int sum=0;
//int c=0; 
int maze::seekonepath(int x,int y,int d,linkstack &stack)
{
	int row,col;
	while(!stack.isempty()){
		x=stack.top->x;		y=stack.top->y;	d=stack.top->d+1;
		stack.pop(stack);
		while(d<4){
			row=x+move[d][0];	col=y+move[d][1];
			if(map[row][col].value == 0){
				if(x==0 && y==1 && d==3)	return 0;
				stack.push(x,y,d);
				if(path_judge(x,y))						//判断岔路口
				{
					map[x][y].cross_road=1;
				}
				else	map[x][y].cross_road=0;
				x=row; y=col; 
				if((x==m-2) && (y==n-1)){
					stack.push(x,y,d);
					sum++;
					return 1;
				}
				else d=0;
			}
			else d++;
		}
	}
	return 0;
}
//递归找出所有的通路
void maze::recursion_findpath(linkstack stack)
{
	cout<<"进入寻找多条路径的函数中"<<endl;
	while(!stack.isempty()){
			if (map[stack.top->x][stack.top->y].cross_road == 1){
				map[m-2][n-1].value=0;
				while(!seekonepath(stack.top->x,stack.top->y,stack.top->d,stack)){	
					break;
				}
		}
		else{
			stack.pop(stack);
		} 
	}
	cout<<"递归查找完毕!"<<endl;
}
int main()
{
	maze m;
	system("color f0");
	m.create_map();
	cout<<"sum="<<sum<<endl;
	return 0;
}

总结

在这次数据结构课程设计中,关于栈的问题主要就是入栈与出栈。栈的特性就是LIFO(先进后出)。其次在迷宫的操纵就是深度优先搜索找出一条路径。在根据这条路径找到所有的路径。从这次数据结构中我学习到了关于数据结构的一些对于栈的操作及对于图的深度优先搜索的方法,在与老师交流的过程中发现许多问题:列如这张地图在处理过程中怎样先把图中不需要走的路径先给堵上。在对于全零的地图处理上,由于按照现实的观点一个人在走迷宫过程中是不可能将并行的两条通路看作是一条通路所以我和老师交流摒弃自己的四宫格遍历方法,实现老师的九宫格方案对地图进行处理。其次在与老师交流过程中,改变自己许多编程上的陋习加上结合自己所学的知识点,无论是从编程风格还是从编程思路上都是对自己的不小的进步。

在思考问题上,始终保持的将代码能够尽可能的简单,并且做到一个函数只做一个功能尽可能将程序的耦合性能够降到最低,能过够便于其他人阅读自己的代码。

在程序设计过程,在开始过程对题目的分析不够透彻。直接将地图设计为一个二维数组并没有对数组进行过多处理导致最后在判断路径的时候造成很多不必要的麻烦代码赘余很难看。和老师交流过后重写了好几次,最终修订下来并对程序做了一些优化。在整个实训过程中,我感觉更加进一步了解到面向对象的设计。

    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/Alearn_/article/details/79762147
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞