用C语言解决迷宫问题

迷宫问题是栈这一块很经典的问题。
迷宫大致可分为三种,简单迷宫、多通路迷宫:通路间不带环、多通路迷宫:通路间带环,其中带环多通路迷宫是最复杂的,解决它,要把栈与递归结合起来,下来我们来一个一个分析吧,先从简单迷宫开始。

简单迷宫
这里写图片描述
要解决这个问题并不难,我们只要从入口进入,当然要先检测这个入口是不是合法并且能不能走的通,如果走的通,把当前这一步的位置入栈,并且把它标记为2,然后继续向当前这一步的其他三个方向走,如果其他方向都走不了,说明上一步走错了,我们要回退,把此时的栈顶元素出栈,并把这一步标为3。
这里写图片描述
这里写图片描述
看了上边两幅图,我相信大家因该很清楚它的过程了吧,下边我们就来看看代码吧。
Maze.h //头文件

#ifndef __MAZE_H__
#define __MAZE_H__

#include<assert.h>
#include<stdio.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
    int _x;
    int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
    int top;
    SDataType _arry[MaxSize];
}Stack;

typedef struct Maze
{
    int _map[Row][Col];
}Maze;

void PassMaze(Maze* m, Position enter);
void InitMaze(Maze* m, int map[Row][Col]);
void PrintMaze(Maze* m);
int IsValidEnter(Maze* m, Position enter);//是否为入口
void StackPush(Stack* ps, SDataType data);
void StackInit(Stack* ps);
void StackPop(Stack* ps);
int IsExit(Position pos, Position enter);//是否为出口
int IsPass(Maze* m, Position pos);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);//返回栈顶元素

#endif //__MAZE_H__

Maze.c //源文件

#include"Maze.h"

void InitMaze(Maze* m, int map[Row][Col])
{
    assert(m != NULL);
    int i = 0;
    int j = 0;
    for (i = 0; i < Row; i++)
    {
        for (j = 0; j < Col; j++)
        {
            m->_map[i][j] = map[i][j];
        }
    }
}

void PrintMaze(Maze* m)
{
    assert(m != NULL);
    int i = 0;
    int j = 0;
    for (i = 0; i < Row; i++)
    {
        for (j = 0; j < Col; j++)
        {
            printf("%d  ", m->_map[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int IsValidEnter(Maze* m, Position enter)
{
    assert(m != NULL);
    if ((enter._x >= 0 || enter._x < Row) && (enter._y >= 0 || enter._y < Col))
    {
        return 1;
    }
    return 0;
}

void StackPush(Stack* ps, SDataType data)
{
    assert(ps != NULL);
    if (ps->top == MaxSize)
        return;
    else
    {
        ps->_arry[ps->top] = data;
        ps->top++;
    }
}

void StackInit(Stack* ps)
{
    assert(ps != NULL);
    ps->top = 0;
}

void StackPop(Stack* ps)
{
    assert(ps != NULL);
    if (ps->top)
        ps->top--;
}

int IsExit(Position pos, Position enter)
{
    if ((pos._x == 0 || pos._y == Row - 1) || (pos._y == 0 || pos._y == Col - 1) && (pos._x !=enter._x || pos._y !=enter._y))
    {
        return 1;
    }
    return 0;
}

int IsPass(Maze* m, Position pos)
{
    assert(m != NULL);
    if (1 == m->_map[pos._x][pos._y])
    {
        return 1;
    }
    return 0;
}

int StackEmpty(Stack* ps)
{
    assert(ps != NULL);
    if (0 == ps->top)
        return 1;//栈空
    return 0;
}

SDataType StackTop(Stack* ps)
{
    assert(ps != NULL);
    return ps->_arry[ps->top - 1];
}

void PassMaze(Maze* m, Position enter)
{
    assert(m != NULL);
    if (!IsValidEnter(m, enter))//如果入口不合法
    {
        printf("入口不合法!!!\n");
        return;
    }
    else
    {
        Stack s;
        StackInit(&s);
        StackPush(&s, enter);
        while (!StackEmpty(&s))//当栈不空时
        {
            Position pos;
            Position next;
            pos = StackTop(&s);
            m->_map[pos._x][pos._y] = 2;//将走过的路标记为2
            if (IsExit(pos, enter))//判断是不是出口
                return;
            else
            {
                //next = pos;
                next._x -= 1;
                if (IsPass(m, next))//判断是否可以走通,只有为1 时才可以走通
                {
                    StackPush(&s, next);
                    continue;
                }
                //左
                next = pos;
                next._y -= 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                //右
                next = pos;
                next._y += 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                //下
                next = pos;
                next._x += 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                m->_map[pos._x][pos._y] = 3;
                StackPop(&s);
            }
        }
    }
}

test.c //测试文件

#include"Maze.h"

void test()
{
    Maze m;
    Position enter;
    enter._x = 5;
    enter._y = 2;
    int map[Row][Col] = { { 0, 0, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 1, 1, 0 },
    { 0, 0, 1, 0, 1, 1 },
    { 0, 0, 1, 0, 0, 0 } };
    InitMaze(&m, map);
    PrintMaze(&m);
    PassMaze(&m, enter);
    PrintMaze(&m);
}

int main()
{
    test();

    return 0;
}

运行结果正确:
这里写图片描述
多通路迷宫:通路间不带环
这里写图片描述
其实解决这个问题也挺简单的,我们只要在找到一个出口后,把出口位置置为0,再让它向回退,就可以找到第二条路径了。
这里写图片描述
我们也来看看它的代码吧:
Maze.h //头文件

#ifndef __MAZE_H__
#define __MAZE_H__

#include<assert.h>
#include<stdio.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
    int _x;
    int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
    int top;
    SDataType _arry[MaxSize];
}Stack;

typedef struct Maze
{
    int _map[Row][Col];
}Maze;

void PassMaze(Maze* m, Position enter);
void InitMaze(Maze* m, int map[Row][Col]);
void PrintMaze(Maze* m);
int IsValidEnter(Maze* m, Position enter);//是否为入口
void StackPush(Stack* ps, SDataType data);
void StackInit(Stack* ps);
void StackPop(Stack* ps);
int IsExit(Position pos, Position enter);//是否为出口
int IsPass(Maze* m, Position pos);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);//返回栈顶元素

#endif //__MAZE_H__

Maze.c //源文件

#include"Maze.h"

void InitMaze(Maze* m, int map[Row][Col])
{
    assert(m != NULL);
    int i = 0;
    int j = 0;
    for (i = 0; i < Row; i++)
    {
        for (j = 0; j < Col; j++)
        {
            m->_map[i][j] = map[i][j];
        }
    }
}

void PrintMaze(Maze* m)
{
    assert(m != NULL);
    int i = 0;
    int j = 0;
    for (i = 0; i < Row; i++)
    {
        for (j = 0; j < Col; j++)
        {
            printf("%d  ", m->_map[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

int IsValidEnter(Maze* m, Position enter)
{
    assert(m != NULL);
    if ((enter._x >= 0 || enter._x < Row) && (enter._y >= 0 || enter._y < Col))
    {
        return 1;
    }
    return 0;
}

void StackPush(Stack* ps, SDataType data)
{
    assert(ps != NULL);
    if (ps->top == MaxSize)
        return;
    else
    {
        ps->_arry[ps->top] = data;
        ps->top++;
    }
}

void StackInit(Stack* ps)
{
    assert(ps != NULL);
    ps->top = 0;
}

void StackPop(Stack* ps)
{
    assert(ps != NULL);
    if (ps->top)
        ps->top--;
}

int IsExit(Position pos, Position enter)
{
    if ((pos._x == 0 || pos._y == Row - 1) || (pos._y == 0 || pos._y == Col - 1) && (pos._x != enter._x || pos._y != enter._y))
    {
        return 1;
    }
    return 0;
}

int IsPass(Maze* m, Position pos)
{
    assert(m != NULL);
    if (1 == m->_map[pos._x][pos._y])
    {
        return 1;
    }
    return 0;
}

int StackEmpty(Stack* ps)
{
    assert(ps != NULL);
    if (0 == ps->top)
        return 1;//栈空
    return 0;
}

SDataType StackTop(Stack* ps)
{
    assert(ps != NULL);
    return ps->_arry[ps->top - 1];
}

void PassMaze(Maze* m, Position enter)
{
    assert(m != NULL);
    if (!IsValidEnter(m, enter))//如果入口不合法
    {
        printf("入口不合法!!!\n");
        return;
    }
    else
    {
        Stack s;
        StackInit(&s);
        StackPush(&s, enter);
        while (!StackEmpty(&s))
        {
            Position pos;
            Position next;
            pos = StackTop(&s);//取当前栈顶元素
            m->_map[pos._x][pos._y] = 2;//将走过的路标记为2
            if (IsExit(pos, enter))
            {
                m->_map[pos._x][pos._y] = 0;//将出口位置置为0
                StackPop(&s);
                continue;
            }
            else
            {
                //next = pos;
                next._x -= 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                //左
                next = pos;
                next._y -= 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                //右
                next = pos;
                next._y += 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                //下
                next = pos;
                next._x += 1;
                if (IsPass(m, next))
                {
                    StackPush(&s, next);
                    continue;
                }
                m->_map[pos._x][pos._y] = 3;//将回退的路标为3
                StackPop(&s);
            }
        }
    }
}

test.c //测试文件

#include"Maze.h"

void test()
{
    Maze m;
    Position enter;
    enter._x = 5;
    enter._y = 2;
    int map[Row][Col] = { { 0, 0, 0, 0, 0, 0 },
    { 0, 0, 1, 1, 1, 1 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 1, 1, 1 },
    { 0, 0, 1, 0, 0, 0 } };
    InitMaze(&m, map);
    PrintMaze(&m);
    PassMaze(&m, enter);
    PrintMaze(&m);
}

int main()
{
    test();

    return 0;
}

运行结果正确
这里写图片描述
最后,我们就来看看最复杂的迷宫:多通路迷宫(通路间带环)
这里写图片描述
在这里我们就不能再让走过的路直接标记为2了,因为如果再按上边那种方法,我们只能找到一条路径,但并不是最短路径,在(4,3)的时候,可以向左走但是走到左边(4,2),(4,2)位置标记为2,它的四边都走不通,只能回退,然后超(4,3)的右走,走的通,标记为2,再向(4,4)的上(不通)、左(不通)、右(通),标记为2,是出口了,出来,虽然出口是找到了,但我们发现这并不是一条最短路径,我们要找到它的最短路径,判断如果是出口,我们就让它出栈,回退,一直找到可以走第二条路为止,我们发现,在(4,1)的右边本来可以走通,找到最短路径,但刚刚我们已经走过它,并吧它标记为2了,所以不能再走了,这样我们就找不到最短路径了,所以像上边两种做法一样,把走过的路标记为2是没有办法解决复杂迷宫的。
这里写图片描述
那我们可以想另外一种办法来解决这个问题,我们可以把入口点标记为2,让每次把它的下一步标记为它的上一步加1,如果它的下一步为1或者大于它,就可以走的通,到出口让它出栈往回退。
在这次和上次不变的是如果走的通,入栈;如果走错了出栈,回退也是出栈,回退的时候,我们也要判断回退的这一步它的四个方向能否走通,因为在刚刚有可能它的其他三个方向可以走通,但我们先检测了它的上方,发现可以走通,我们就超上走了,其他可以走通的方向并没有走,所以在回退的时候我们要检测,这样就可以找到最短路径了。
这里写图片描述
我们就先来分析一下这个代码的框架吧。
这里写图片描述
在代码里我们还有很多细节,我们再来细分一下吧。
在判断是出口的时候,我们要出口先入栈,因为这时只是判断了,还并没有入栈,然后再保存最短路径,这就出现了一个问题,我们什么时候保存最短路径呢?在走迷宫的时候,我们每走一步,我们都把它的坐标保存到栈里了,所以我们保存最短路径的时候,就要比较当前栈里的元素个数与保存最短路径的栈里元素个数,如果size(Pah())

    //判断是否为出口,若为出口,保存最短路径,Pop
    if (IsExit(m, cur, enter))
    {
        StackPush(Path, cur);
        if (StackSize(Path) < StackSize(shortPath) || StackEmpty(shortPath))
        {
            SaveshortPath(shortPath, Path);
        }
        StackPop(Path);
        //return;
    }

void SaveshortPath(Stack* shortPath, Stack* Path)
{
    assert(Path);
    assert(shortPath);
    int i = 0;
    int size = 0;
    size = StackSize(Path);
    for (i = 0; i < size; i++)
    {
        shortPath->_arry[i] = Path->_arry[i];
    }
    shortPath->top = size;
}

判断下一步能否走通(我们在前边也分析了,只要下一步比上一步的值大也可以走通):

int IsPass(Maze* m, Position cur, Position next)
{
    assert(m != NULL);
    if (m->_map[next._x][next._y] > m->_map[cur._x][cur._y] || m->_map[next._x][next._y] == 1)
        return 1;

    return 0;
}

下来,我们看一下完整的代码吧:
Maze.h //头文件
我们要定义一个栈的结构体,每次入栈的元素类型是坐标形式的,所以还要定义一个Position的结构体。

#ifndef __MAZE_H__
#define __MAZE_H__

#include<stdio.h>
#include<assert.h>

#define MaxSize 20
#define Row 6
#define Col 6

typedef struct Position
{
    int _x;
    int _y;
}Position;

typedef Position SDataType;

typedef struct Stack
{
    SDataType _arry[MaxSize];
    int top;
}Stack;

typedef struct Maze
{
    int _map[Row][Col];
}Maze;

void PrintMaze(Maze* m);
void InitMaze(Maze* m, int map[Row][Col]);
void PassMaze(Maze* m, Position enter, Stack* shortPath);
void StackInit(Stack* ps);
void StackPush(Stack* ps, SDataType data);
void StackPop(Stack* ps);
int IsExit(Maze* m, Position pos, Position enter);
int StackEmpty(Stack* ps);
SDataType StackTop(Stack* ps);
int IsValidEnter(Maze* m, Position enter);
int StackSize(Stack* ps);
int IsPass(Maze* m, Position cur, Position next);
void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath);
void SaveshortPath(Stack* Path, Stack* shortPath);

#endif //__MAZE_H__

Maze.c //源文件
还有很多细节,比如我们在定义一个栈之后我们要把它初始化,不然就会出错。
在void PassMaze(Maze* m, Position enter, Stack* shortPath)函数中,我们只需要把地图,入口,和保存最短路径的栈传进去就行,因为我们左后需要的就是最短路径。

void PassMaze(Maze* m, Position enter, Stack* shortPath)
{
    Stack Path;
    StackInit(&Path);
    assert(m != NULL);
    if (!IsValidEnter(m, enter))//如果入口不合法
    {
        printf("入口不合法!!!\n");
        return;
    }
    else
        _GetMazeshortPath(m, enter, enter, &Path, shortPath);
}

在void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath)函数中,我们还要把最开始的入口点传入,因为我们要判断当前位置是否为出口,判断出口的方法就是在判断是入口的基础上加上不能为入口这一条,我们还要传入Path()这个栈的指针,因为我们要保存当前路径。

void _GetMazeshortPath(Maze* m, Position cur, Position enter, Stack* Path, Stack* shortPath)
{
    Position next;
    //判断是否为出口,若为出口,保存最短路径,Pop
    if (IsExit(m, cur, enter))
    {
        StackPush(Path, cur);
        if (StackSize(Path) < StackSize(shortPath) || StackEmpty(shortPath))
        {
            SaveshortPath(shortPath, Path);
        }
        StackPop(Path);
        //return;
    }
    if (StackEmpty(Path))
        m->_map[enter._x][enter._y] = 2;
    StackPush(Path, cur);

    //上
    next = cur;
    next._x -= 1;
    if (IsPass(m, cur, next))
    {
        m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;//标记
        _GetMazeshortPath(m, next, enter, Path, shortPath);
    }

    //左
    next = cur;
    next._y -= 1;
    if (IsPass(m, cur, next))
    {
        m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
        _GetMazeshortPath(m, next, enter, Path, shortPath);
    }


    //右
    next = cur;
    next._y += 1;
    if (IsPass(m, cur, next))
    {
        m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
        _GetMazeshortPath(m, next, enter, Path, shortPath);
    }

    //下
    next = cur;
    next._x += 1;
    if (IsPass(m, cur, next))
    {
        m->_map[next._x][next._y] = m->_map[cur._x][cur._y] + 1;
        _GetMazeshortPath(m, next, enter, Path, shortPath);
    }

    //说明上步走错了
    StackPop(Path);
}

test.c //测试文件

#include"Maze.h"

void test()
{
    Position enter;
    enter._x = 5;
    enter._y = 1;
    Maze m;
    Stack shortPath;
    StackInit(&shortPath);
    int map[Row][Col] = {
    { 0, 0, 0, 0, 0, 0 },
    { 0, 1, 1, 1, 0, 0 },
    { 0, 1, 0, 1, 0, 0 },
    { 0, 1, 0, 1, 0, 0 },
    { 0, 1, 1, 1, 1, 1 },
    { 0, 1, 0, 0, 0, 0 } };
    InitMaze(&m, map);
    PrintMaze(&m);
    PassMaze(&m, enter, &shortPath);
    PrintMaze(&m);
}

int main()
{
    test();

    return 0;
}

运行结果正确:
这里写图片描述

猜你喜欢

转载自blog.csdn.net/huaijiu123/article/details/82025487
今日推荐