【数据结构】迷宫问题求解(链栈,DFS)

1.问题描述:

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

2.基本要求:

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

(2)编写递归形式的算法,求得迷宫中所有可能的通路。以方阵形式输出迷宫及其通路。

3.原理:

①. 链栈:用链式结构实现的栈,即只能从栈顶进出的链表,需要用到的函数有:栈的初始化:voidInit_S(Linkstack &S);判断链栈是否为空: intIsEmpty(Linkstack S);压栈:void Push(Linkstack&S,SElemType e);出栈:void Push(Linkstack&S,SElemType e)

②.  DFS遍历思想:定义一地图数组map[N],一标记数组vis[N],一方向数组dir[4][2]= {{1,0},{-1,0},{0,1},{0,-1}};表示下,上,右,左四个方向(因为二维数组是个矩阵)。从起点往终点方向走,遇到障碍停下(map[x]为障碍标记1时),走过处进行标记(vis[x]改为1)。一直走到终点。 

4.思路:

 ①a.对于非递归,首先初始化一个栈,将起点的坐标放入栈中,并使此处标记数组的值变为1(开始都初始化为0)。进入循环,当栈不空时,取栈顶,若不为终点坐标,则在周围四个方向进行搜索,满足条件(1不为障碍,2未走过)即放入栈中,将此坐标标记数组改为1,并用pre数组记录前驱,若为终点坐标,则输出这一组解,函数终止。b.由于要以三元组形式输出,计算方向即成了关键。可用先前存储的pre前驱数组来进行计算。由于从栈顶依次输出,顺序与走迷宫顺序相反,故应从高下标端向低下标断打印。

②a.对于递归打印所有情况,调用DFS函数,若到达终点,则调用OutPut2函数,输出此方案,否则,对四个方向遍历,递归调用DFS,(每次调用完成后vis回溯至0),直至输出所有情况.b.由于要用矩阵形式输出迷宫走法(通路),故在计算方向的时候用D,U,R,L分别替代通路上的坐标(分别表示DOWN,UP,RIGHT,LEFT)。因此需先将mp数组变为字符数组mp2。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define maxn 1000
using namespace std;
int n,m;//n*m迷宫 
int flag=0; //若有能到终点的路,则flag变为1
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}}; //方向数组 ,分别为下,上,右,左 ,输出分别设为0,1,2,3
int vis[maxn][maxn];//DFS标记数组 
int mp[maxn][maxn];  //输入的迷宫 
char mp2[maxn][maxn]; //待输出的迷宫 
typedef struct Point
{
    int x,y;
}SElemType;
SElemType pre[maxn][maxn];//记录每个位置的前驱
SElemType solution[maxn*maxn];//方案记录数组
 
//链栈定义及操作 
typedef struct Snode
{
    SElemType data;
    struct Snode *next;
}Snode,*Linkstack;

void Init_S(Linkstack &S)//初始化
{
    S=(Linkstack)malloc(sizeof(Snode));
    S->next=NULL;
}

int IsEmpty(Linkstack S) //判断链栈是否为空
{
    if(S->next==NULL) return 1; //1表示为空 
	else return 0;
}

void Push(Linkstack &S,SElemType e) //压栈 
{
    Linkstack p=(Linkstack)malloc(sizeof(Snode));
    p->data=e;
	p->next=S->next;
    S->next=p;
}

void Pop(Linkstack &S,SElemType &e) //出栈 
{
    
    if(S->next==NULL) return ;
    Linkstack p=S->next;
    e=p->data;
    S->next=p->next;
    free(p);
}

int Check(SElemType u)  //检查点是否有障碍和是否已经遍历过 
{
	if(mp[u.x][u.y]==0&&vis[u.x][u.y]==0)
		return 1;
	else return 0;
}

void Input() //输入迷宫 
{
	memset(mp,1,sizeof(mp));
	for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            cin>>mp[i][j];
}

void Output() //输出迷宫 
{
	printf("迷宫为:\n\n");
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j) 
			cout<<mp[i][j];
        cout<<endl;
    }
    cout<<endl;	
}

int Direction(SElemType a,SElemType b)  //节点a->b的方向(方向数组做了解释) 
{
	for(int i=0;i<4;++i)
		if(b.x==a.x+dir[i][0]&&b.y==a.y+dir[i][1])
			return i;
}

void Output1(int inx,int iny,int outx,int outy) //以三元组形式输出一条通路
{
	
    int len=0;
    SElemType p={outx,outy};
    while(!(p.x==inx&&p.y==iny))
    {
        solution[++len]={p.x,p.y};
        p=pre[p.x][p.y];
    }
    solution[++len]={inx,iny};
    for(int i=len;i>=2;--i)
    {
        printf("(%d,%d,%d)\n",solution[i].x,solution[i].y,Direction(solution[i],solution[i-1]));
    }
    printf("(%d,%d,OK)\n\n",n,m);//到达终点 
}

void Solve1(int a,int b,int c,int d)
{
	cout<<"项目1:非递归输出:"<<endl;
	memset(vis,0,sizeof(vis));
	Linkstack S;
	Init_S(S);
	Push(S,{a,b});
	vis[a][b]=1;
	while(!IsEmpty(S))
	{
		SElemType u,v;
		Pop(S,u); //取栈顶
		if(u.x==c&&u.y==d){
			Output1(a,b,c,d); //到达终点输出一组解 
			return ;   //并终止函数 (按题意)
		} 
		for(int i=0;i<4;i++){
			v.x=u.x+dir[i][0];
			v.y=u.y+dir[i][1];
			if(!Check(v)) continue;
			vis[v.x][v.y]=1;
			pre[v.x][v.y]=u;
			Push(S,v);
		}
	}
	cout<<"无通路,请重新输入"<<endl;
}

void Output2(int len) 
{
	for(int i=1;i<=n;++i)
        for(int j=1;j<=m;++j)
            mp2[i][j]=mp[i][j]+48;
    for(int i=1;i<len;++i)
    {
        int x=solution[i].x,y=solution[i].y,d=Direction(solution[i],solution[i+1]);
        if(d==0) mp2[x][y]='D';
        if(d==1) mp2[x][y]='U';
        if(d==2) mp2[x][y]='R';
        if(d==3) mp2[x][y]='L';    //DOWN,UP,RIGHT,LEFT
    }
    flag=1;
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=m;++j)
            printf("%c ",mp2[i][j]);
        printf("\n");
    }
    printf("\n");
}

void DFS(int k,int inx,int iny,int outx,int outy) 
{
	/*k表示当前走到第几步,x,y表示当前的位置*/
    solution[k]={inx,iny};
    vis[inx][iny]=1;
    if(inx==outx&&iny==outy) Output2(k);//如果到了终点就输出此方案
    else
    for(int i=0;i<4;++i)//四个方向遍历
    {
        int u=inx+dir[i][0],v=iny+dir[i][1];
        if(!Check({u,v})) continue;
        DFS(k+1,u,v,outx,outy);
    }
    vis[inx][iny]=0;//回溯,vis信息清零
}

void Solve2(int a,int b,int c,int d)
{
	cout<<"项目1:递归输出:"<<endl;
	flag=0;
	memset(vis,0,sizeof(vis));
	DFS(1,a,b,c,d);
	if(!flag) cout<<"无通路,请重新输入"<<endl;
}

int main(){
	freopen("text1.txt","r",stdin); 
	while(cin>>n>>m)
	{
		int a,b,c,d;
		cout<<"输入起点、终点:"<<endl; //(a,b)起点,(b,c)终点
		cin>>a>>b>>c>>d; 
		Input();
		Output();
		Solve1(a,b,c,d);
		Solve2(a,b,c,d);
		cout<<"xxxxxxxxxxxxxxxxxxxxxx"<<endl;
	} 
	
	return 0;
} 

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