一般迷宫问题的求解

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;
}
    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/SCaryon/article/details/73521622
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞