【顺序栈应用】迷宫求解

本文采用C语言应用顺序栈实现对迷宫问题的求解(走迷宫其实也蛮有意思的)


【问题描述】

采用“穷举求解”方法,从起点出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路返回,换一个方向再继续探索。为了保证在任何位置上都能沿原路返回,需要用一个后进先出的顺序栈结构来保存从起点到当前位置的路径。

迷宫如图所示(代码中也可以自己更改):

迷宫

(代码中'#'表示墙壁,‘ ’代表可以通过)


【算法思想】

迷宫求解

假设“当前位置”指的是”在搜索过程中某一时刻所在迷宫图中的某个方块的位置“,则求迷宫的一条路径的算法的基本思想可为:

1.若当前位置”可通“,则纳入”当前路径”,并继续朝”下一位置探索“,即切换”下一位置“为”当前位置“,如此重复直到到达出口;

2.若当前位置”不可通“,则应顺着”来向“退回到”前一通道块“。然后朝着除”来向“之外的其他方向继续探索;

3.若该通道块的四周方块均”不可通“,则应从”当前路径“上删除该通道块。

所谓”下一位置“指的是”当前位置“的四周4个方向上(东、南、西、北)相邻的方块。假设以栈S记录”当前路径“,则栈顶存放的是”当前路径上最后一个通道块“,由此,”纳入路径“的操作即为”当前位置入栈“;”从当前路径上删除前一通道块“的操作即为”出栈“。

算法思想

下面首先来熟悉一下栈的实现,top为指向栈顶元素的下一位置的指针,base为指向栈底元素的指针


一、栈类型的定义:

typedef struct
{
    SElemType *top;
    SElemType *base;
    int stacksize;
}Stack;


二、栈的相关操作

1、栈的初始化
//栈初始化
void InitStack(Stack **b)
{
    (*b)->base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
    if (!b)  exit(OVERFLOW);
    (*b)->top = (*b)->base;
    (*b)->stacksize = MAXSIZE;
}

2、判断栈空

//栈空
Status StackEmpty(Stack *b)
{
    if (b->top == b->base)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}

3、判断栈满
//栈满
Status StackFull(Stack *b)
{
    if (((b->top) - (b->base)) >= b->stacksize)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}

4、入栈
//入栈
void Push(Stack **b, SElemType e)
{
    if (StackFull(*b))
    {
        exit(OVERFLOW);
    }
    *(*b)->top = e;
    (*b)->top = (*b)->top + 1;
}

5、出栈

//出栈
void Pop(Stack **b, SElemType *e)
{
    if (StackEmpty(*b))
    {
        exit(ERROR);
    }
    (*b)->top = (*b)->top - 1;
    *e = *(*b)->top;
}


三、定义地图


//地图,‘#’指墙壁
char map[MAPROW + 2][MAPCOL + 2] = {
    {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
    {'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
    {'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
    {'#', ' ', ' ', ' ', ' ', '#', '#', ' ', ' ', '#'},
    {'#', ' ', '#', '#', '#', ' ', ' ', ' ', ' ', '#'},
    {'#', ' ', ' ', ' ', '#', ' ', ' ', ' ', ' ', '#'},
    {'#', ' ', '#', ' ', ' ', ' ', '#', ' ', ' ', '#'},
    {'#', ' ', '#', '#', '#', ' ', '#', '#', ' ', '#'},
    {'#', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'},
    {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
};


//迷宫中的坐标位置
typedef struct pos
{
    int x;
    int y;
}pos;
typedef struct current
{
    pos seat;//当前位置
    int direction;//走向下一通道块的方向,顺时针方向:direction=1,2,3,4分别为东,南,西,北
}current;
typedef current SElemType;


pos start = { 1,1 };//设置迷宫起点
pos end = { MAPROW,MAPCOL };//设置迷宫终点


四、实现迷宫相应操作

1、对曾经经过的路径设置标记‘1',方便寻找最短路径

//对曾经经过的路径设置标记`‘1'`,方便寻找最短路径
void beforesign(pos curpos)
{
    map[curpos.x][curpos.y] = '1';
}

2、对于曾经走过虽不是墙壁但是不通的路,标记'@'
//对于曾经走过虽不是墙壁但是不通的路,标记'@'
void nosign(pos curpos)
{
    map[curpos.x][curpos.y] = '@';
}

3、返回下一位置坐标
方向

//返回下一位置坐标
pos nextpos(pos curpos, int dir)
{
    switch (dir)
    {
        case 1://向东走,行号不变,列号加一
            curpos.y = curpos.y + 1;
            break;
        case 2://向南走
            curpos.x = curpos.x + 1;
            break;
        case 3://向西走
            curpos.y = curpos.y - 1;
            break;
        case 4://向北走
            curpos.x = curpos.x - 1;
            break;
    }
    return curpos;
}

4、判断当前位置是否可通过,即为' ',而不是'#'墙,‘@’死路,‘1’已走过

//判断当前位置是否可通过,即为' ',而不是'#'墙,‘@’死路,‘1’已走过
bool pass(pos curpos)
{
    if (map[curpos.x][curpos.y] == ' ')
    {
        return true;
    }
    else
    {
        return false;
    }
}

5、看迷宫中是否存在一条从入口到出口的路,存放在栈中

注意使用结构体前一定要分配空间,即
Stack *s; s = (Stack*)malloc(sizeof(Stack));


//看迷宫中是否存在一条从入口到出口的路,存放在栈中
bool path(pos start,pos end)
{
    Stack *s;
    s = (Stack*)malloc(sizeof(Stack));
    InitStack(&s);
    pos curpos = start;//设置当前位置为入口
    if (pass(curpos))//如果该位置可以通过
    {
        beforesign(curpos);//作标记‘#’
        current cur;
        cur.seat = curpos;
        cur.direction = 1;
        Push(&s, cur);//将当前的方向和位置压栈
        curpos = nextpos(curpos, 1);//下一位置定为当前位置的东侧
    }
    while (!StackEmpty(s))
    {
        if (pass(curpos))//如果该位置可以通过
        {
            beforesign(curpos);//作标记‘#’
            current cur;
            cur.seat = curpos;
            cur.direction = 1;
            Push(&s, cur);//将当前的方向和位置压栈
            if ((curpos.x == end.x) && (curpos.y == end.y))//到达终点停止
            {
                return true;
            }
            curpos = nextpos(curpos, 1);//下一位置定为当前位置的东侧
        }
        else//如果该位置不能通过,则栈顶元素出栈,即刚刚走过的迷宫块退出
        {
            current cur;
            Pop(&s, &cur);//弹出栈顶元素,并将该元素存在cur中
            while ((cur.direction == 4) && (!StackEmpty(s)))
            {
                nosign(cur.seat);//标记该块四周均不可通
                Pop(&s, &cur);//继续弹出上一块的栈顶位置(即上一步的位置),存在cur中
            }
            if (cur.direction < 4)//如果弹出的当前位置还有别的方向没有探索,则继续探索
            {
                cur.direction = cur.direction + 1;//试探下一个方向
                Push(&s, cur);//将该方向的位置压入栈中
                curpos = nextpos(cur.seat, cur.direction);//计算在该方向的下一位置
            }
        }
    }
    return false;
}


四、输出地图相关函数

1、打印迷宫

//打印迷宫
void printmap()
{
    for (int i = 0; i < MAPROW + 2; i++)
    {
        for (int j = 0; j < MAPCOL + 2; j++)
        {
            printf("%c", map[i][j]);//打印完整的迷宫
        }
        printf("\n");
    }
    printf("\n");
}

2、仅打印通过迷宫的路径

//仅打印通过迷宫的路径
void printpath()
{
    for (int i = 0; i < MAPROW + 2; i++)
    {
        for (int j = 0; j < MAPCOL + 2; j++)
        {
            if ((map[i][j] == '#') || (map[i][j] == '1'))//仅打印墙壁和走过的迷宫路径
            {
                printf("%c", map[i][j]);
            }
            else
            {
                printf(" ");
            }
        }
        printf("\n");
    }
    printf("\n");
}


五、main函数测试


int main()
{
    printf("迷宫的初始状态为:\n");
    printmap();
    if (path(start, end))//如果有路径
    {
        printf("迷宫有路径可通过\n");
        printf("迷宫标识后的状态为:\n");
        printmap();
        printf("通过迷宫的路径为:\n");
        printpath();
    }
    else
    {
        printf("迷宫不存在可通过的路径\n");
    }
    return 0;
}


六、程序运行结果

(1)
(2)
(3)

七、完整代码(附注释)


#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#define MAPROW 8
#define MAPCOL 8
typedef int Status;
//迷宫中的坐标位置
typedef struct pos
{
    int x;
    int y;
}pos;
typedef struct current
{
    pos seat;//当前位置
    int direction;//走向下一通道块的方向,顺时针方向:direction=1,2,3,4分别为东,南,西,北
}current;
typedef current SElemType;
typedef struct
{
    SElemType *top;
    SElemType *base;
    int stacksize;
}Stack;
//栈初始化
void InitStack(Stack **b)
{
    (*b)->base = (SElemType*)malloc(MAXSIZE * sizeof(SElemType));
    if (!b)  exit(OVERFLOW);
    (*b)->top = (*b)->base;
    (*b)->stacksize = MAXSIZE;
}
//栈空
Status StackEmpty(Stack *b)
{
    if (b->top == b->base)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}
//栈满
Status StackFull(Stack *b)
{
    if (((b->top) - (b->base)) >= b->stacksize)
    {
        return OK;
    }
    else
    {
        return ERROR;
    }
}
//入栈
void Push(Stack **b, SElemType e)
{
    if (StackFull(*b))
    {
        exit(OVERFLOW);
    }
    *(*b)->top = e;
    (*b)->top = (*b)->top + 1;
}
//出栈
void Pop(Stack **b, SElemType *e)
{
    if (StackEmpty(*b))
    {
        exit(ERROR);
    }
    (*b)->top = (*b)->top - 1;
    *e = *(*b)->top;
}
//地图,‘#’指墙壁
char map[MAPROW + 2][MAPCOL + 2] = {
    {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
    {'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
    {'#', ' ', ' ', '#', ' ', ' ', ' ', '#', ' ', '#'},
    {'#', ' ', ' ', ' ', ' ', '#', '#', ' ', ' ', '#'},
    {'#', ' ', '#', '#', '#', ' ', ' ', ' ', ' ', '#'},
    {'#', ' ', ' ', ' ', '#', ' ', ' ', ' ', ' ', '#'},
    {'#', ' ', '#', ' ', ' ', ' ', '#', ' ', ' ', '#'},
    {'#', ' ', '#', '#', '#', ' ', '#', '#', ' ', '#'},
    {'#', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'},
    {'#', '#', '#', '#', '#', '#', '#', '#', '#', '#'}
};
pos start = { 1,1 };//设置迷宫起点
pos end = { MAPROW,MAPCOL };//设置迷宫终点
//对曾经经过的路径设置标记‘1',方便寻找最短路径
void beforesign(pos curpos)
{
    map[curpos.x][curpos.y] = '1';
}
//对于曾经走过虽不是墙壁但是不通的路,标记'@'
void nosign(pos curpos)
{
    map[curpos.x][curpos.y] = '@';
}
//返回下一位置坐标
pos nextpos(pos curpos, int dir)
{
    switch (dir)
    {
        case 1://向东走,行号不变,列号加一
            curpos.y = curpos.y + 1;
            break;
        case 2://向南走
            curpos.x = curpos.x + 1;
            break;
        case 3://向西走
            curpos.y = curpos.y - 1;
            break;
        case 4://向北走
            curpos.x = curpos.x - 1;
            break;
    }
    return curpos;
}
//判断当前位置是否可通过,即为' ',而不是'#'墙,‘@’死路,‘1’已走过
bool pass(pos curpos)
{
    if (map[curpos.x][curpos.y] == ' ')
    {
        return true;
    }
    else
    {
        return false;
    }
}
//看迷宫中是否存在一条从入口到出口的路,存放在栈中
bool path(pos start,pos end)
{
    Stack *s;
    s = (Stack*)malloc(sizeof(Stack));
    InitStack(&s);
    pos curpos = start;//设置当前位置为入口
    if (pass(curpos))//如果该位置可以通过
    {
        beforesign(curpos);//作标记‘#’
        current cur;
        cur.seat = curpos;
        cur.direction = 1;
        Push(&s, cur);//将当前的方向和位置压栈
        curpos = nextpos(curpos, 1);//下一位置定为当前位置的东侧
    }
    while (!StackEmpty(s))
    {
        if (pass(curpos))//如果该位置可以通过
        {
            beforesign(curpos);//作标记‘#’
            current cur;
            cur.seat = curpos;
            cur.direction = 1;
            Push(&s, cur);//将当前的方向和位置压栈
            if ((curpos.x == end.x) && (curpos.y == end.y))//到达终点停止
            {
                return true;
            }
            curpos = nextpos(curpos, 1);//下一位置定为当前位置的东侧
        }
        else//如果该位置不能通过,则栈顶元素出栈,即刚刚走过的迷宫块退出
        {
            current cur;
            Pop(&s, &cur);//弹出栈顶元素,并将该元素存在cur中
            while ((cur.direction == 4) && (!StackEmpty(s)))
            {
                nosign(cur.seat);//标记该块四周均不可通
                Pop(&s, &cur);//继续弹出上一块的栈顶位置(即上一步的位置),存在cur中
            }
            if (cur.direction < 4)//如果弹出的当前位置还有别的方向没有探索,则继续探索
            {
                cur.direction = cur.direction + 1;//试探下一个方向
                Push(&s, cur);//将该方向的位置压入栈中
                curpos = nextpos(cur.seat, cur.direction);//计算在该方向的下一位置
            }
        }
    }
    return false;
}
//打印迷宫
void printmap()
{
    for (int i = 0; i < MAPROW + 2; i++)
    {
        for (int j = 0; j < MAPCOL + 2; j++)
        {
            printf("%c", map[i][j]);//打印完整的迷宫
        }
        printf("\n");
    }
    printf("\n");
}
//仅打印通过迷宫的路径
void printpath()
{
    for (int i = 0; i < MAPROW + 2; i++)
    {
        for (int j = 0; j < MAPCOL + 2; j++)
        {
            if ((map[i][j] == '#') || (map[i][j] == '1'))//仅打印墙壁和走过的迷宫路径
            {
                printf("%c", map[i][j]);
            }
            else
            {
                printf(" ");
            }
        }
        printf("\n");
    }
    printf("\n");
}
int main()
{
    printf("迷宫的初始状态为:\n");
    printmap();
    if (path(start, end))//如果有路径
    {
        printf("迷宫有路径可通过\n");
        printf("迷宫标识后的状态为:\n");
        printmap();
        printf("通过迷宫的路径为:\n");
        printpath();
    }
    else
    {
        printf("迷宫不存在可通过的路径\n");
    }
    return 0;
}


越努力,越幸运

end~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容