C++ 数据结构求解迷宫问题(栈)

一、问题描述

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

二、基本要求

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

三、求解思路

1、定义迷宫数据结构: 使用一个二维数组来表示迷宫,其中0表示通路,1表示障碍。另外,我们需要追踪迷宫中的访问情况,以及路径上的步骤和方向信息。

2、使用非递归方式搜索路径: 可以使用一个栈来辅助搜索迷宫的路径。

3、输出路径信息: 当找到终点时,栈中保存了从起点到终点的路径信息。我们可以依次从栈中取出路径上的点坐标和方向信息,并输出为三元组的形式(i,j,d),表示每一步的位置和方向。

4、处理无通路情况: 如果栈为空而仍未找到终点,则说明没有从起点到终点的通路。

5、其他辅助函数: 在程序中还需要实现一些辅助函数,比如判断下一个位置是否合法,初始化迷宫,输出迷宫等。这些函数在搜索路径时会被调用。

四、求解代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;//对于操作返回值以及链栈进行宏定义
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define MAXSIZE 100
#include <stdio.h>
/*----------------------------------*/
/*数据类型的定义*/
typedef int Status;
typedef int Boolean;
typedef struct{
    int x,y,dir;
}point; // 点坐标的xy,以及下一步方向dir定义

typedef struct node{
    point point;
    struct node* next;
}Node; // 对于链栈中节点的定义
typedef Node ElemType;
typedef struct{
    Node * top;
    Node * bottom;
}Linkstack,*pLinkstack; // 对于链栈整体的定义

int NoSoutionReason = 0; //1 代表起点为障碍,2代表重点为障碍
int mp[MAXSIZE][MAXSIZE],n,m; // m,n表示迷宫规模,mp用来保存迷宫
int mpcopy[MAXSIZE][MAXSIZE]; // mpcopy用来保存和输出任务2所要求的路径
int visit[MAXSIZE][MAXSIZE];  // visit数组用来保存地图中某点是否访问过
point solution[MAXSIZE];  // solution用来保存任务2的遍历序列
int dx[] = {0,1,0,-1}; // dx为x方向坐标移动数组
int dy[] = {1,0,-1,0}; // dy为y方向坐标移动数组
int StartX = 0,StartY = 0;
int EndX = 0, EndY = 0;

/*----------------------------------*/

/*----------------------------------*/

   // 链栈的操作
    // 初始化,判空,进栈,出栈,取栈顶,求栈长操作。
Status Init(Linkstack &S)
{
    Node * p = (Node *)malloc(sizeof(Node));
    if(!p) return ERROR;
    S.bottom = S.top = p;
    p->next = NULL;
    return OK;
}
Status IsEmpty(Linkstack S)
{
    if(S.bottom == S.top) return TRUE;
    else return FALSE;
}
Status Push(Linkstack & S, ElemType e)
{
    Node * p = (Node *)malloc(sizeof(Node));
    if(!p) return ERROR;
    p->point.x = e.point.x;
    p->point.y = e.point.y;
    p->point.dir = e.point.dir;
    p->next = S.top;
    S.top =  p;
    return OK;
}
Status Pop( Linkstack & S,ElemType & e)
{
    if(S.bottom == S.top) return ERROR;
    Node * p = S.top;
    e.point.x  = p->point.x;
    e.point.y  = p->point.y;
    e.point.dir = p->point.dir;
    S.top = p->next;
    free(p);
    return OK;
}
Status GetTop(Linkstack S, ElemType &e)
{
    if(S.bottom == S.top) return ERROR;
    else {
        e.point.x = S.top->point.x;
        e.point.y = S.top->point.y;
        e.point.dir = S.top->point.dir;
    }
    return OK;
}
Status GetLength(Linkstack S)
{
    Node * p = S.top;
    int cnt = 0;
    while(p != S.bottom){
        cnt++;
        p = p->next;
    }
    return cnt;
}

/*----------------------------------*/
//迷宫求解部分
Status OutPut_Path_1(Linkstack S)
{
    //输出(x,y,dir)三元组来表示路径
    if(S.top == S.bottom) return ERROR;
    printf("一条从入口到出口的通路为:");
    Node * p = S.top;
    Linkstack Stemp; //临时栈
    Init(Stemp);
    Node nodetemp;
    /*将栈S中的序列逆置到Stemp中*/
    while(p != S.bottom){
        nodetemp.point.x = p->point.x;
        nodetemp.point.y = p->point.y;
        nodetemp.point.dir = p->point.dir;
        Push(Stemp,nodetemp);
        p=p->next;
    }
    p = Stemp.top;
    //输出栈Stemp
    while(p!= Stemp.bottom){
        printf("(%d,%d,%d) ",p->point.x,p->point.y,p->point.dir);
        p = p->next;
    }
    printf("\n\n");
    return OK;
}
void Output_mg()
{
    //输出迷宫
    for(int i = 0; i<m; ++i){
        for(int j = 0; j<n; ++j){
            //printf("%d %d\n",i,j);
            if(i == StartX && j == StartY && mp[i][j] != 1){
                printf("S");
                continue;
            }
            if(i == EndX && j == EndY && mp[i][j] != 1 ){
                printf("E");
                continue;
            }
            if(mp[i][j] == 1) printf("1");
            else printf("0");
        }
        printf("\n");
    }
}
bool check(int x ,int y)
{
    /*检查xy坐标是否越界,以及mp[x][y]是否符合访问规则*/
    if(x<=0||x>m||y<=0||y>n||visit[x][y] == 1||mp[x][y] == 1)
        return false;
    return true;
}
bool Output_one(Linkstack &S)
{
    //用非递归输出1条路径
    if(mp[StartX][StartY] == 1){
        printf("当前迷宫无通路\n");
        printf("因为入口%d %d为障碍\n",StartX,StartY);
        return false;
    }  //若起点为1, 特判
    if(mp[EndX][EndY] == 1) {
        printf("当前迷宫无通路\n");
        printf("因为出口%d %d为障碍\n",EndX,EndY);
        return false;
    }    //若终点为1, 特判
    point temp = {StartX,StartY,0};
    Node nodetemp = {temp,NULL},nodemem;
    Init(S);
    Push(S,nodetemp);
    visit[StartX][StartY] = 1;
    while(!IsEmpty(S)){
        GetTop(S,nodetemp);
        bool isfind = false;
        if(nodetemp.point.x == EndX && nodetemp.point.y == EndY)
            return true;
        for(int i = 0; i<4;++i){
            int nextx = nodetemp.point.x+dx[i];
            int nexty = nodetemp.point.y+dy[i];
            if(check(nextx,nexty)){
                int dir = i+1;
                Pop(S,nodemem);
                nodemem.point.dir = dir;
                Push(S,nodemem);
                nodetemp.point.x = nextx;
                nodetemp.point.y = nexty;
                nodetemp.point.dir = 0;
                visit[nextx][nexty] = 1;
                Push(S,nodetemp);
                isfind = true;
                break;
            }
        }
        if(!isfind) Pop(S,nodetemp);
    }
    printf("当前迷宫无通路\n");
    return false;
}
void OutPut_Path_2(int id)
{
    /*
        输出所有可能的路径
        1.首先将原mp拷贝给mpcopy
        2.按照solution数组给mpcopy做好标记
        3.输出mpcopy数组
    */
    for(int i = 0 ; i<m;++i)
        for(int j = 0 ; j<n;++j)
            mpcopy[i][j] = mp[i][j];
    for(int i = 0; i<=id;++i){
        point p = solution[i];
        mpcopy[p.x][p.y] = p.dir;
    }
    for(int i = 0 ; i<m;++i){
        for(int j = 0 ; j<n;++j){
            if(i == StartX && j == StartY){
                 printf(" S");
                 continue;
            }
            if(i == EndX && j == EndY){
                printf(" E");
                continue;
            }
            if(mpcopy[i][j] == 2) printf("→");
            else if(mpcopy[i][j] == 3) printf("↓");
            else if(mpcopy[i][j] == 4) printf("←");
            else if(mpcopy[i][j] == 5) printf("↑");
            else if(mpcopy[i][j] == 1) printf(" 1");
            else printf(" 1");
        }
        printf("\n");
    }
    printf("\n");
}
void dfs(int id , int x, int y)
{
    /*
        输出所有可能的路径
        此处dfs搜索的过程
    */
    visit[x][y] = 1;
    for(int i = 0;i<4;++i){
        int nextx = x+dx[i];
        int nexty = y+dy[i];
        if(check(nextx,nexty)){
            visit[nextx][nexty] = 1;//置为访问
            solution[id] = {x,y,i+2};
            if(nextx == EndX && nexty == EndY){
                OutPut_Path_2(id);
                visit[EndX][EndY] = 0;
                return;
            }
            dfs(id+1,nextx,nexty);
            visit[nextx][nexty] = 0;//回溯时置为未访问
        }
    }
}
void Output_all()
{
    /*
        输出所有可能的路径
        此处为调用dfs函数的过程
    */
    memset(visit,0,sizeof(visit));
    printf("所有可能的通路为:\n");
    dfs(0,StartX,StartY);

}
void Input_mg()
{
    /*输入要处理的地图*/
    memset(mp,0,sizeof(mp));
    memset(visit,0,sizeof(visit));
    printf("请输入迷宫:\n");
    for(int i = 0; i<m;++i){
        for(int j = 0; j<n;++j){
            scanf("%d",&mp[i][j]);
        }
    }
}
/*----------------------------------*/
int main()
{

        Linkstack S;
        printf("请输入迷宫的长和宽:\n")
        scanf("%d %d",&m,&n);
        int NoSoutionReason = 0;
        printf("/*----------------------------------*/\n");
        Input_mg();
        printf("请输入起点坐标:\n");
        scanf("%d %d",&StartX,&StartY);
        printf("请输入终点坐标:\n");
        scanf("%d %d",&EndX,&EndY);
        printf("\n");
        Output_mg();
        if(Output_one(S)){
            OutPut_Path_1(S);
            Output_all();
        }
        printf("\n");
    return 0;
}

 五、运行结果


 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值