1.问题描述:
以一个m*n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。设计一个程序,对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。
2.基本要求
(1)首先实现一个以链表作存储结构的栈类型,然后编写一个求解迷宫的非递归程序。求得的通路以三元组(i,j,d)的形式输出。其中:(i,j)指示迷宫中的一个坐标,d表示走到下一坐标的方向。如,对于教材第50页图3.4所示的迷宫,输出一条通路为:(1,1,1),(1,2,2),(2,2,2),(3,2,3),(3,1,2),…。
(2)编写递归形式的算法,求得迷宫中所有可能的通路。
(3)以方阵形式输出迷宫及其通路。
这里除了书上要求的利用栈完成遍历,还有一个利用队列完成遍历并进行路径回收的算法
1.用链栈完成图的遍历并保存路径
基本思路:
由于DFS是盲目的,所以没有办法知道那个点可能到达最后的终点。所以需要引入一个father指针,指向某一点的前驱。在最后找到目标点时,再利用其父节点进行回溯
对于每一个可能的点入栈之后,专门一个指针指向它的父节点。
然后在进行pop的操作时,不将任何一个点free掉,否则father指针将会变成野指针,无法完成作用。
基本结构
typedef struct Node2{
int x,y,d;
struct Node2 *next,*prior;
struct Node2 *father;//father指针是回溯用的
}Node2;
typedef struct{
Node2 *head;
Node2 *top;
}LinkStack;
基本函数
初始化
LinkStack* Init(LinkStack *s)
{
s=(LinkStack*)malloc(sizeof(LinkStack*));
s->head=(Node2*)malloc(sizeof(Node2*));
s->top=s->head;
s->head->x=s->head->y=0;
s->head->next=s->head->prior=NULL;
s->head->father=s->head;
return s;
}
Push操作
LinkStack* Push(LinkStack *s,Node2 *father,int x,int y)
{
Node2 *p=(Node2*)malloc(sizeof(Node2));
p->x=x;
p->y=y;
p->prior=s->top;
p->father=father;
p->next=NULL;
p->d=0;
s->top->next=p;
s->top=s->top->next;
return s;
}
Pop操作
LinkStack* Pop(LinkStack *s)
{
Node2 *p=s->top;
s->top=p->prior;
//free(p);不能将p点free掉,否则无法完成回溯
return s;
}
取栈顶元素
Node2* front(LinkStack *s,int &x,int &y)
{
Node2 *p=s->top;
x=p->x;
y=p->y;
return s->top;
}
指针回溯
Node2* Back(LinkStack* s)
{
return s->top->father;
}
判断栈是否空
bool isempty(LinkStack *s)
{
if(s->top==s->head)
return true;
return false;
}
DFS部分
void dfs1(int x,int y,int tarx,int tary)
{
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
bool flag=false;
LinkStack* s=Init(s);
s=Push(s,s->head,x,y);//将起点压栈
visit[x][y]=false;
int dx,dy;
while(!isempty(s))
{
Node2 *father=front(s,dx,dy);
if(dx==tarx&&dy==tary){//达到终点,跳出循环
flag=true;
break;
}
s=Pop(s);
for(int i=0;i<4;++i){
dx+=dis[i][0];
dy+=dis[i][1];
if(check(dx,dy)){
s=Push(s,father,dx,dy);//可能到达的地方压栈
visit[dx][dy]=false;
}
}
}
if(!flag)
cout<<"没有通路"<<endl;
else{
int cnt=0;
Road rpp[maxn];
while(1)
{
if(s->top->x==x&&s->top->y==y){
rpp[cnt].x=x;
rpp[cnt].y=y;
rpp[cnt].d=s->top->d;
break;
}
Node2 *p=s->top->father;
if(s->top->x>p->x)//根据上一个点判断是哪个方向
s->top->father->d=3;
else if(s->top->x<p->x)
s->top->father->d=1;
else if(s->top->y>p->y)
s->top->father->d=2;
else
s->top->father->d=4;
rpp[cnt].x=s->top->x;//点的信息放入三元组
rpp[cnt].y=s->top->y;
rpp[cnt].d=s->top->d;
cnt++;
s->top=Back(s);
}
for(int i=cnt;i>=0;--i)
cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;
}
}
递归完成图的遍历并输出所有通路
基本思想
要输出所有通路,其实就是把一个最简单的DFS添加一个DFS深度参数和一个路径保存数组。每次DFS都会更新数组,如果到达目标点,就可以把结果输出,然后去找下一个可能的路径。
基本结构
路径保存结构
typedef struct{
int x,y,d;
}Road;
基本函数
方向判断
int dir(int x1,int y1,int x2,int y2)
{
if(x1<x2)
return 1;
else if(x1>x2)
return 3;
else if(y1<y2)
return 2;
else
return 4;
}
输出路径
void out(int len)
{
char mp1[110][110];
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
mp1[i][j]=mp[i][j]+'0';
}
for(int i=0;i<=len;++i){
if(sol[i].d==1)
mp1[sol[i].x][sol[i].y]='|';
else if(sol[i].d==2)
mp1[sol[i].x][sol[i].y]='-';
else if(sol[i].d==3)
mp1[sol[i].x][sol[i].y]='|';
else
mp1[sol[i].x][sol[i].y]='-';
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
cout<<mp1[i][j];
cout<<endl;
}
cout<<endl;
}
DFS部分
void dfs2(int x,int y,int tarx,int tary,int k)//添加一个参数k代表深度
{
sol[k].x=x;//记录路径
sol[k].y=y;
sol[k-1].d=dir(sol[k-1].x,sol[k-1].y,sol[k].x,sol[k].y);//记录方向
if(x==tarx&&y==tary){
out(k);
return ;
}
visit2[x][y]=false;
int dx=x,dy=y;
for(int i=0;i<4;++i){
dx+=dis[i][0];
dy+=dis[i][1];
if(check2(dx,dy))
dfs2(dx,dy,tarx,tary,k+1);
}
visit2[x][y]=true;
}
利用队列完成遍历和路径回溯
基本思想
利用队列当然就是BFS,只不过出队并不能真正的删除数据,而且也需要一个father指针指向其父节点,最后跳回起点,完成路径回溯
基本结构
队列元素
typedef struct{
int x,y;
int father;
}Node;
队列
typedef struct{
Node data[maxn];
int l,r;
}Queue;
路径记录
typedef struct{
int x,y,d;
}Road;
BFS部分
void bfs(int x,int y,int tarx,int tary)
{
int pos;
Queue que;
Road rpp[maxn];
visit2[x][y]=false;
Node a;
bool flag=false;
que.l=que.r=0;
a.x=x;a.y=y;a.father=0;
que.data[que.r++]=a;
while(que.l!=que.r)//当队列不空
{
Node b=que.data[que.l];
if(b.x==tarx&&b.y==tary){
pos=que.l;
flag=true;
break;
}
que.l++;
for(int i=0;i<4;++i){//遍历四个方向
Node c;
int dx=dis[i][0];
int dy=dis[i][1];
c.x=b.x;
c.y=b.y;
c.x+=dx;
c.y+=dy;
c.father=que.l-1;
if(check2(c.x,c.y)){
que.data[que.r++]=c;
visit2[c.x][c.y]=false; //及时进行标记
}
}
}
if(!flag)
cout<<"达不到出口"<<endl;
else{
int cnt=0;
while(1)//路径回溯
{
rpp[cnt].x=que.data[pos].x;
rpp[cnt].y=que.data[pos].y;
if(rpp[cnt].x==x&&rpp[cnt].y==y){//到达出发点
cnt++;
break;
}
int d=0,nextpos=que.data[pos].father;
Node k=que.data[nextpos];
if(que.data[pos].x>k.x)//根据上一个点判断是哪个方向
d=3;
else if(que.data[pos].x<k.x)
d=1;
else if(que.data[pos].y>k.y)
d=2;
else
d=4;
rpp[cnt].d=d;
pos=que.data[pos].father;//回溯
cnt++;
}
for(int i=cnt-1;i>=0;--i)
cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;
}
}
全部代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
typedef struct{
int x,y;
int father;
}Node;
typedef struct{
Node data[maxn];
int l,r;
}Queue;
typedef struct{
int x,y,d;
}Road;
typedef struct Node2{
int x,y,d;
struct Node2 *next,*prior;
struct Node2 *father;
}Node2;
typedef struct{
Node2 *head;
Node2 *top;
}LinkStack;
int mp[110][110];
bool visit1[110][110];
bool visit2[110][110];
Road sol[maxn];
int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int m,n;
LinkStack* Init(LinkStack *s)
{
s=(LinkStack*)malloc(sizeof(LinkStack*));
s->head=(Node2*)malloc(sizeof(Node2*));
s->top=s->head;
s->head->x=s->head->y=0;
s->head->next=s->head->prior=NULL;
s->head->father=s->head;
return s;
}
LinkStack* Push(LinkStack *s,Node2 *father,int x,int y)
{
Node2 *p=(Node2*)malloc(sizeof(Node2));
p->x=x;
p->y=y;
p->prior=s->top;
p->father=father;
p->next=NULL;
p->d=0;
s->top->next=p;
s->top=s->top->next;
return s;
}
LinkStack* Pop(LinkStack *s)
{
Node2 *p=s->top;
s->top=p->prior;
//free(p);不能将p点free掉,否则无法完成回溯
return s;
}
Node2* front(LinkStack *s,int &x,int &y)
{
Node2 *p=s->top;
x=p->x;
y=p->y;
return s->top;
}
Node2* Back(LinkStack* s)
{
return s->top->father;
}
bool isempty(LinkStack *s)
{
if(s->top==s->head)
return true;
return false;
}
void print() //输出图
{
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
cout<<mp[i][j]<<" ";
cout<<endl;
}
cout<<endl;
}
bool check1(int x,int y) //防越界处理
{
if(x>=1&&x<=n&&y>=1&&y<=m)
if(visit1[x][y])
return true;
return false;
}
bool check2(int x,int y) //防越界处理
{
if(x>=1&&x<=n&&y>=1&&y<=m)
if(visit2[x][y])
return true;
return false;
}
void out(int len)
{
char mp1[110][110];
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
mp1[i][j]=mp[i][j]+'0';
}
for(int i=0;i<=len;++i){
if(sol[i].d==1)
mp1[sol[i].x][sol[i].y]='|';
else if(sol[i].d==2)
mp1[sol[i].x][sol[i].y]='-';
else if(sol[i].d==3)
mp1[sol[i].x][sol[i].y]='|';
else
mp1[sol[i].x][sol[i].y]='-';
}
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j)
cout<<mp1[i][j];
cout<<endl;
}
cout<<endl;
}
int dir(int x1,int y1,int x2,int y2)
{
if(x1<x2)
return 1;
else if(x1>x2)
return 3;
else if(y1<y2)
return 2;
else
return 4;
}
void dfs2(int x,int y,int tarx,int tary,int k)
{
sol[k].x=x;
sol[k].y=y;
sol[k-1].d=dir(sol[k-1].x,sol[k-1].y,sol[k].x,sol[k].y);
if(x==tarx&&y==tary){
out(k);
return ;
}
visit2[x][y]=false;
int dx=x,dy=y;
for(int i=0;i<4;++i){
dx+=dis[i][0];
dy+=dis[i][1];
if(check2(dx,dy))
dfs2(dx,dy,tarx,tary,k+1);
}
visit2[x][y]=true;
}
void bfs(int x,int y,int tarx,int tary)
{
int pos;
Queue que;
Road rpp[maxn];
visit2[x][y]=false;
Node a;
bool flag=false;
que.l=que.r=0;
a.x=x;a.y=y;a.father=0;
que.data[que.r++]=a;
while(que.l!=que.r)//当队列不空
{
Node b=que.data[que.l];
if(b.x==tarx&&b.y==tary){
pos=que.l;
flag=true;
break;
}
que.l++;
for(int i=0;i<4;++i){//遍历四个方向
Node c;
int dx=dis[i][0];
int dy=dis[i][1];
c.x=b.x;
c.y=b.y;
c.x+=dx;
c.y+=dy;
c.father=que.l-1;
if(check2(c.x,c.y)){
que.data[que.r++]=c;
visit2[c.x][c.y]=false; //及时进行标记
}
}
}
if(!flag)
cout<<"达不到出口"<<endl;
else{
int cnt=0;
while(1)//路径回溯
{
rpp[cnt].x=que.data[pos].x;
rpp[cnt].y=que.data[pos].y;
if(rpp[cnt].x==x&&rpp[cnt].y==y){//到达出发点
cnt++;
break;
}
int d=0,nextpos=que.data[pos].father;
Node k=que.data[nextpos];
if(que.data[pos].x>k.x)//根据上一个点判断是哪个方向
d=3;
else if(que.data[pos].x<k.x)
d=1;
else if(que.data[pos].y>k.y)
d=2;
else
d=4;
rpp[cnt].d=d;
pos=que.data[pos].father;//回溯
cnt++;
}
for(int i=cnt-1;i>=0;--i)
cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;
}
}
void dfs1(int x,int y,int tarx,int tary)
{
bool flag=false;
LinkStack* s=Init(s);
s=Push(s,s->head,x,y);//将起点压栈
visit1[x][y]=false;
int dx,dy;
while(!isempty(s))
{
Node2 *father=front(s,dx,dy);
if(dx==tarx&&dy==tary){//达到终点,跳出循环
flag=true;
break;
}
s=Pop(s);
for(int i=0;i<4;++i){
dx+=dis[i][0];
dy+=dis[i][1];
if(check1(dx,dy)){
s=Push(s,father,dx,dy);//可能到达的地方压栈
visit1[dx][dy]=false;
}
}
}
if(!flag)
cout<<"没有通路"<<endl;
else{
int cnt=0;
Road rpp[maxn];
while(1)
{
if(s->top->x==x&&s->top->y==y){
rpp[cnt].x=x;
rpp[cnt].y=y;
rpp[cnt].d=s->top->d;
break;
}
Node2 *p=s->top->father;
if(s->top->x>p->x)//根据上一个点判断是哪个方向
s->top->father->d=3;
else if(s->top->x<p->x)
s->top->father->d=1;
else if(s->top->y>p->y)
s->top->father->d=2;
else
s->top->father->d=4;
rpp[cnt].x=s->top->x;//点的信息放入三元组
rpp[cnt].y=s->top->y;
rpp[cnt].d=s->top->d;
cnt++;
s->top=Back(s);
}
for(int i=cnt;i>=0;--i)
cout<<"("<<rpp[i].x<<","<<rpp[i].y<<","<<rpp[i].d<<")"<<endl;
}
}
int main()
{
freopen("in.txt","r",stdin);
int x,y,tarx,tary;
memset(visit1,true,sizeof(visit1));
memset(visit2,true,sizeof(visit2));
memset(mp,1,sizeof(mp));
cout<<"输入n和m,起点坐标和目标点坐标"<<endl;
cin>>n>>m>>x>>y>>tarx>>tary;
cout<<"输入图"<<endl;
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
cin>>mp[i][j];
if(mp[i][j]){
visit2[i][j]=false;
visit1[i][j]=false;
}
}
dfs1(x,y,tarx,tary);
cout<<endl;
dfs2(x,y,tarx,tary,0);
return 0;
}