现在的位置: 首页 > 综合 > 正文

栈的应用之迷宫求解

2014年09月28日 ⁄ 综合 ⁄ 共 5612字 ⁄ 字号 评论关闭

迷宫求解的思路很简单,即所谓的“穷举求解”,从入口出发,顺某一方向探索,若能走通,则继续往前走,否则沿着原路退回,换一个方向继续探索,直至所有可能的通路都探索到为止。

为了保证在任何位置上都能沿着原路返回,需要使用栈来保存从入口到当前位置的路径。
这里采用之前介绍的顺序栈作为容器存储路径。
具体实现:
/****************************************
使用栈进行迷宫求解
by Rowandjj
2014/4/10
*****************************************/
#include<iostream>
using namespace std;

#define STACK_INIT_SIZE 100
#define STACKINCREMENT 10
#define OK 1
#define ERROR 0
#define OVERFLOW 0
#define TRUE 1
#define FALSE 0

//------------------迷宫求解----------------------------------
typedef struct _POSTYPE_//坐标类型
{
    int x;//行
    int y;//列
}PosType;

#define MAXLENGTH 25
typedef int MazeType[MAXLENGTH][MAXLENGTH];

MazeType m;//迷宫数组
int curstep = 1;//当前足迹,初值为1

typedef struct _SELEMTYPE_//栈元素类型
{
    int ord;//序号
    PosType seat;//当前坐标
    int di;//下一方向(0-3分别代表东南西北)
}SElemType;
//-------------------栈定义------------------------------------
typedef int Status;
typedef struct _STACK_//栈的顺序存储结构
{
    SElemType *base;//栈底指针
    SElemType *top;//栈顶指针,始终指在栈顶下一个元素
    int stacksize;//栈容量
}SqStack;
/*栈操作定义*/
Status InitStack(SqStack *S);
Status DestroyStack(SqStack *S);
Status ClearStack(SqStack *S);
Status StackEmpty(SqStack S);
int StackLength(SqStack S);
Status GetTop(SqStack S,SElemType *e);
Status Push(SqStack *S,SElemType e);
Status Pop(SqStack *S,SElemType *e);
Status StackTraverse(SqStack S,Status(*visit)(SElemType));

Status visit(SElemType e);
/*栈实现*/
Status InitStack(SqStack *S)
{
    (*S).base = (SElemType*)malloc(sizeof(SElemType)*STACK_INIT_SIZE);
    if(!(*S).base)
    {
        exit(OVERFLOW);
    }
    (*S).top = (*S).base;
    (*S).stacksize = STACK_INIT_SIZE;
    return OK;
}

Status DestroyStack(SqStack *S)
{
    free((*S).base);
    (*S).base = (*S).top = NULL;
    (*S).stacksize = 0;
    return OK;
}
Status ClearStack(SqStack *S)
{
    (*S).top = (*S).base;
    return OK;
}
Status StackEmpty(SqStack S)
{
    if(S.top == S.base)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
int StackLength(SqStack S)
{
    return S.top-S.base;
}
Status GetTop(SqStack S,SElemType *e)
{
    *e = *(--S.top);
    return OK;
}

Status Push(SqStack *S,SElemType e)
{
    if((*S).top - (*S).base >= STACK_INIT_SIZE)
    {
        (*S).base = (SElemType *)realloc((*S).base,(STACK_INIT_SIZE + STACKINCREMENT)*sizeof(SElemType));
        if(!(*S).base)
        {
            exit(OVERFLOW);
        }

        (*S).top = (*S).base+(*S).stacksize;
        (*S).stacksize += STACKINCREMENT;
    }
    *((*S).top)++ = e;
    return OK;
}
Status Pop(SqStack *S,SElemType *e)
{
    if((*S).top == (*S).base)
    {
        return ERROR;
    }
    *e = *--(*S).top;
    return OK;
}
Status StackTraverse(SqStack S,Status(*vi)(SElemType))
{
    while(S.top>S.base)
    {
        vi(*S.base++);
    }
    cout<<endl;
    return OK;
}

Status visit(SElemType e)
{
    cout<<"("<<e.seat.x<<","<<e.seat.y<<")"<<" ";
    return OK;
}
//---------------迷宫求解算法-------------------------

//定义墙元素值为0,可通过路径为1,不能通过路径为-1,通过路径为足迹
Status Pass(PosType b)//判断当前路径是否可以通过
{
    if(m[b.x][b.y] == 1)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}
void FootPrint(PosType a)//使迷宫m的a点的序号变为足迹(curstep)
{
    m[a.x][a.y] = curstep;
}

PosType NextPos(PosType c,int di)//根据当前位置及移动方向,返回下一位置
{
    PosType direc[4] = {{0,1},{1,0},{0,-1},{-1,0}};//行增量,列增量
    c.x += direc[di].x;
    c.y += direc[di].y;
    return c;
}

void MarkPrint(PosType b)//使迷宫m的b点的序号变为-1(不能通过的路径)
{
    m[b.x][b.y] = -1;
}
//若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶),并返回TRUE;否则返回FALSE
Status MazePath(PosType start,PosType end)
{
    SqStack S;
    PosType curpos;
    SElemType e;

    InitStack(&S);
    curpos = start;

    do 
    {
        if(Pass(curpos))//当前位置可通
        {
            FootPrint(curpos);//留下足迹
            
            e.ord = curstep;
            e.seat.x = curpos.x;
            e.seat.y = curpos.y;
            e.di = 0;
            
            Push(&S,e);//入栈当前位置及状态

            curstep++;

            if(curpos.x == end.x && curpos.y == end.y)//当前位置是出口
            {
                cout<<"路径如下"<<endl;
                StackTraverse(S,visit);
            
                return TRUE;
            }
            curpos = NextPos(curpos,e.di);//当前若不是出口则继续判断下一个位置
        }else//当前位置不可通
        {
            if(!StackEmpty(S))
            {
                Pop(&S,&e);//当前位置出栈
                curstep--;
                while(e.di == 3 && !StackEmpty(S))//前一位置处于最后一个方向(北)
                {
                    MarkPrint(e.seat);
                    Pop(&S,&e);
                    curstep--;
                }
                if(e.di < 3)//还有其他方向没有探索
                {
                    e.di++;//换下一个方向探索
                    Push(&S,e);//改变方向后重新入栈
                    curstep++;
                    curpos = NextPos(e.seat,e.di);

                }
            }
        }

    } while (!StackEmpty(S));
    
    return FALSE;
}

测试:

//--------------------测试------------------------------------
void Print(int x,int y)
{ /* 打印迷宫 */
    int i,j;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
            printf("%3d",m[i][j]);
        printf("\n");
    }
 }
int main()
{
    PosType begin,end;
    int i,j,x,y,x1,y1;
    cout<<"请输入迷宫的行数,列数(包括外墙):";
    cin>>x;
    cin>>y;
    for(i=0;i<x;i++) /* 定义周边值为0(同墙) */
    {
        m[0][i]=0; /* 行周边 */
        m[x-1][i]=0;
    }
    for(j=1;j<y-1;j++)
    {
        m[j][0]=0; /* 列周边 */
        m[j][y-1]=0;
    }
    for(i=1;i<x-1;i++)
        for(j=1;j<y-1;j++)
            m[i][j]=1; /* 定义通道初值为1 */
    cout<<"请输入迷宫内墙单元数:";
    cin>>j;
    cout<<"请依次输入迷宫内墙每个单元的行数,列数:\n";
    for(i=1;i<=j;i++)
    {
        cin>>x1;
        cin>>y1;
        m[x1][y1]=0; /* 定义墙的值为0 */
    }
    cout<<"迷宫结构如下:\n";
    Print(x,y);
    cout<<"请输入起点的行数,列数:";
    cin>>begin.x;
    cin>>begin.y;
    cout<<"请输入终点的行数,列数:";
    cin>>end.x;
    cin>>end.y;
    if(MazePath(begin,end)) /* 求得一条通路 */
    {
        cout<<"此迷宫从入口到出口的一条路径如下:\n";
        Print(x,y); /* 输出此通路 */
    }
    else
        printf("此迷宫没有从入口到出口的路径\n");
    return 0;
}

运行结果:



解:

栈中保存的路径:

这里还可以使用递归来求出所有的解
实现方式:

#include<IOSTREAM>
using namespace std;

typedef struct _POSTYPE_
{
    int x;//行值
    int y;//列值
}PosType;

#define MAXLENGTH 25
typedef int MazeType[MAXLENGTH][MAXLENGTH];//迷宫类型

PosType end;//终点
MazeType m;//迷宫
int x,y;//迷宫行数,列数
/* 定义墙元素值为0,可通过路径为-1,通过路径为足迹 */

void Print(int x,int y)
{ /* 输出解 */
    int i,j;
    for(i=0;i<x;i++)
    {
        for(j=0;j<y;j++)
            printf("%3d",m[i][j]);
        printf("\n");
    }
    printf("\n");
 }

void Try(PosType cur,int curstep)//试探
{
    int i;
    PosType next;//下一位置
    PosType direc[4] = {{0,1},{1,0},{0,-1},{-1,0}};//依次为东南西北
    for(i = 0 ; i <= 3; i++)
    {
        next.x = cur.x+direc[i].x;
        next.y = cur.y+direc[i].y;
        if(m[next.x][next.y] == -1)//是通路
        {
            m[next.x][next.y] = ++curstep;
            if(next.x != end.x || next.y != end.y)//若不是终点继续递归调用
            {
                Try(next,curstep);
            }
            else
            {
                system("pause");
                Print(x,y);//输出结果
            }
            m[next.x][next.y] = -1;//恢复为通路,试探下一条路
            curstep--;
        }
    }
}


int main()
{
    PosType begin;
    int i,j,x1,y1;
    printf("请输入迷宫的行数,列数(包括外墙):");
    cin>>x;
    cin>>y;
    for(i=0;i<x;i++) /* 定义周边值为0(同墙) */
    {
        m[0][i]=0; /* 行周边 */
        m[x-1][i]=0;
    }
    for(j=1;j<y-1;j++)
    {
        m[j][0]=0; /* 列周边 */
        m[j][y-1]=0;
    }
    for(i=1;i<x-1;i++)
    for(j=1;j<y-1;j++)
        m[i][j]=-1; /* 定义通道初值为-1 */
    printf("请输入迷宫内墙单元数:");
    cin>>j;
    if(j)
        printf("请依次输入迷宫内墙每个单元的行数,列数:\n");
    for(i=1;i<=j;i++)
    {
        cin>>x1;
        cin>>y1;
        m[x1][y1]=0;
    }
    printf("迷宫结构如下:\n");
    Print(x,y);
    printf("请输入起点的行数,列数:");
    cin>>begin.x;
    cin>>begin.y;

    printf("请输入终点的行数,列数:");
    cin>>end.x;
    cin>>end.y;
    m[begin.x][begin.y]=1;
    Try(begin,1); /* 由第一步起点试探起 */
    return 0;
}

抱歉!评论已关闭.