迷宫问题

问题描述
迷宫:一个m×n的网络,有很多墙壁,对前进方向形成了多处障碍。先通过某种方法获得到出口的路线,最短路线
示意图:以6×6网络为例

这里写图片描述

解决方法:

回溯法
概念:
回溯法又称试探法;具体指一步一步向前试探,当某一步有多种选择时,可以先任意选择一种,只要这种选择暂时可行就继续向前,一旦发现到达某步后无法再继续前进,说明前面已经做的选择可能有问题,就可以后退,回到上一步重新选择,这样的方法称为回溯
几种迷宫求解:

迷宫1—-简单迷宫(一条通路)

具体思路:

假设从入口点开始,先探测向上方向,当向上方向没有路可走时,检测其余几个方向,当四个方向都无路可走时,退回到上一步,探测上一位置的其他方向是否有路可走(下图中绿色的箭头表示回溯到上一步,蓝色箭头表示探测到路的路线)

这里写图片描述

代码实现:

//检查是否是通路
/*1.在边界范围内
  2.是有效路径*/
int MazeCheckIsAccess(Pos pos)
{
    if (pos._row >= 0 && pos._col < N
        &&pos._col >= 0 && pos._col < N
        &&maze[pos._row][pos._col] == 1)
            return 1;
        else
            return 0;
}
int MazeGetPath(Pos entry)
{
    //入口点
    Pos cur = entry;

    Stack s;
    StackInit(&s);

    //压栈,将走过的有效坐标保存到栈中
    StackPush(&s,cur);


    while (StackEmpty(&s) != 0)
    {
        //cur = StackTop(&s);

        //将走过的有效通路进行标记
        maze[cur._row][cur._col] = 2;

        //记录下探测的位置
        Pos next = cur;


        if (next._col == N - 1)
        {
            //当走到墙的边界时,表示已经到达出口,开始返回
            return 1;
        }


        //检查上
        next._row -= 1;
        if (MazeCheckIsAccess(next) == 1)
        {
            StackPush(&s, next);
            cur = next;
            continue;
        }


        //检查下
        next = cur;
        next._row += 1;
        if (MazeCheckIsAccess(next) == 1)
        {
            StackPush(&s, next);
            cur = next;
            continue;
        }


        //检查左
        next = cur;
        next._col -= 1;
        if (MazeCheckIsAccess(next) == 1)
        {
            StackPush(&s, next);
            cur = next;
            continue;
        }


        //检查右
        next = cur;
        next._col += 1;
        if (MazeCheckIsAccess(next) == 1)
        {
            StackPush(&s, next);
            cur = next;
            continue;
        }

        //死胡同
        StackPop(&s);

    }
    return 0;

}

这是一种迭代的方式寻找通路,下面用递归在来捋一下这个思路

递归思想:将问题转化为更多规模更小的子问题(如下图)

这里写图片描述

代码实现:

//检查是否是通路
/*1.在边界范围内
  2.是有效路径*/
int MazeCheckIsAccess(Pos pos)
{
    if (pos._row >= 0 && pos._col < N
        &&pos._col >= 0 && pos._col < N
        &&maze[pos._row][pos._col] == 1)
            return 1;
        else
            return 0;
}
int MazeGetPathR(Pos entry)
{
    //入口点
    Pos cur = entry;

    //将有效通路进行标记
    maze[cur._row][cur._col] = 2;
    if (cur._col == N - 1)
    {
        //判断是否到达墙的边界,到达边界表示找到出口
        return 1;
    }


    //检查上
    cur._row -= 1;
    if (MazeCheckIsAccess(cur) == 1)
    {
        if (MazeGetPathR(cur))
        {
            return 1;
        }
    }

    //检查下
    cur = entry;
    cur._row += 1;
    if (MazeCheckIsAccess(cur) == 1)
    {
        if (MazeGetPathR(cur))
        {
            return 1;
        }
    }

    //检查左
    cur = entry;
    cur._col -= 1;
    if (MazeCheckIsAccess(cur) == 1)
    {
        if (MazeGetPathR(cur))
        {
            return 1;
        }
    }

    //检查右
    cur = entry;
    cur._col += 1;
    if (MazeGetPathR(cur) == 1)
    {
        if (MazeGetPathR(cur))
        {
            return 1;
        }
    }

    return 0;

}
迷宫2———多通路迷宫

问题描述:迷宫一般都有多条通路,这时我们需要找到一条最短的路径来找到出口

思路:同上面所说一条通路的迷宫类似,同样采用标记的方法来寻找出口;多条通路的情况,我们借助栈来保存最短路径的坐标;并且在递归的方式上我们也做了一些调整,当找到通路时,不在直接返回,而是返回到上一步继续探测其他方向有没有通路,直至走完所有的通路,并且将最短的路径保存到栈中

这里写图片描述

最短路径的保存:

将最短路径保存到栈中,这里我们借助两个栈来>实现,一个是最短路径的栈(shortPath),一个是通路的栈(Path)当找到通路时,我们将通路的坐标保存到Panth中,比较shortPath和Path的大小,将较短的路径保存到shortPath当中,当找完所有通路,取出栈shortPath的内容,就是最短的路径

栈的复制,首先看一下代码:

    if (shortPath._array)
    {
      free(shortPath._array);

    }
    shortPath._array =(DataType*)malloc(sizeof(DataType)* path->_top);
    memcpy(shortPath._array, path->_array, sizeof(DataType)*path->_top);
    shortPath._top = path->_top;
    shortPath._end = path->_top;

分析:将路径保存到shortPath中,就要保证栈中没有内容,故而第一步先检查shortPath中有没有内容,如果有就将其释放;第二步就将其较短路径拷贝到shortPath中,在这里一般都会这样写:shortPath=Path;但这种方式是不可取的,一旦这样写,可能将原本要拷贝的内容修改,此时我们需要将其内容逐步拷贝

完整代码:

Stack shortPath;//最短路径,用来保存路径

//检查是否是通路
/*1.在边界范围内
  2.是有效路径*/
int MazeCheckIsAccess(Pos pos)
{
    if (pos._row >= 0 && pos._col < N
        &&pos._col >= 0 && pos._col < N
        &&maze[pos._row][pos._col] == 1)
            return 1;
        else
            return 0;
}
void MazeGetShortPath(Pos entry, Stack* path)
{

    Pos cur = entry;
    //用栈保存通路坐标
    StackPush(path, cur);

    maze[cur._row][cur._col] = 2;
    if (cur._col == N - 1)
    {
        printf("找到出口:%d,%d\n", cur._row, cur._col);
        if (!(StackEmpty(&shortPath)) || StackSize(path) < StackSize(&shortPath))
        {

            //深浅拷贝问题
            if (shortPath._array)
            {
                free(shortPath._array);
            }
            //将最短路径保存到shortPath中
            shortPath._array =(DataType*)malloc(sizeof(DataType)* path->_top);
            memcpy(shortPath._array, path->_array, sizeof(DataType)*path->_top);
            shortPath._top = path->_top;
            shortPath._end = path->_top;
        }
    }
    //检查上
    cur._row -= 1;
    if (MazeCheckIsAccess(cur) == 1)
    {
        MazeGetShortPath(cur, path);

    }

    //检查下
    cur = entry;
    cur._row += 1;
    if (MazeCheckIsAccess(cur) == 1)
    {
        MazeGetShortPath(cur, path);

    }
        //检查左
        cur = entry;
        cur._col -= 1;
        if (MazeCheckIsAccess(cur) == 1)
        {
            MazeGetShortPath(cur, path);

        }

        //检查右
        cur = entry;
        cur._col += 1;
        if (MazeCheckIsAccess(cur) == 1)
        {
            MazeGetShortPath(cur, path);

        }

        //没有通路
        StackPop(path);
}
迷宫3———-迷宫带环

思路:迷宫带环指迷宫当中的通路有带环的现象,此时,在去用同一个数据标记所走过的路就会重复走,也找不到最短的路径,在这里我们换一种思路,改变标记的数据,当前位置标记数=前一位置标记数+1,当探测到下一位置的数字比当前位置大时便可继续走,从而找到最短通路(具体过程如下图)

这里写图片描述

具体代码:

int MazeCheckShortIsAccess(Pos next,Pos cur)
{

    //检测下一位置是否可以继续走
    /*1.在边界范围内
      2.是有效路径
      3.下一位置标记数>当前位置标记数*/
    if (next._row >= 0 && next._col < N
        &&next._col >= 0 && next._col < N
        &&maze[next._row][next._col] == 1||
        maze[next._row][next._col] > maze[cur._row][cur._col])
        return 1;
    else
        return 0;
}
void MazeGetShortPath1(Pos entry, Stack* path)
{

    Pos cur = entry;
    Pos next = cur;


    if (StackEmpty(path) == 0)
    {
        //入口点标记为2
        maze[next._row][next._col] = 2;
    }
    else
    {
        //其余有效位置+1
        Pos prev = StackTop(path);
        maze[next._row][next._col] = maze[prev._row][prev._col] + 1;
    }

    //用栈保存路径坐标
    StackPush(path, next);

    if (next._col == N - 1)
    {
        printf("找到出口:%d,%d\n", next._row, next._col);
        if (!(StackEmpty(&shortPath)) || StackSize(path) < StackSize(&shortPath))
        {

            //深浅拷贝问题
            if (shortPath._array)
            {
                free(shortPath._array);
            }

            //选出最短的路径存放到最短路径栈中
            shortPath._array = (DataType*)malloc(sizeof(DataType)* path->_top);
            memcpy(shortPath._array, path->_array, sizeof(DataType)*path->_top);
            shortPath._top = path->_top;
            shortPath._end = path->_top;
        }
    }


    /*采用递归方法探测,转化为子问题*/
    //检查上
    next = entry;
    next._row -= 1;
    if (MazeCheckShortIsAccess(next, cur) == 1)
    {
        MazeGetShortPath1(next, path);
    }

    //检查下
    next = entry;
    next._row += 1;
    if (MazeCheckShortIsAccess(next, cur) == 1)
    {
        MazeGetShortPath1(next, path);
    }
    //检查左
    next = cur;
    next._col += 1;
    if (MazeCheckShortIsAccess(next, cur) == 1)
    {
        MazeGetShortPath1(next, path);
    }

    //检查右
    next = cur;
    next._col += 1;
    if (MazeCheckShortIsAccess(next, cur) == 1)
    {
        MazeGetShortPath1(next, path);
    }

    //没有通路
    StackPop(path);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值