数据结构-回溯法解决迷宫问题

原创 2018年04月15日 20:35:59

回溯法:对一个包括有很多个结点,每个结点有若干个搜索分支的问题,把原问题分解为若干个子问题求解的算法;当搜索到某个结点发现无法再继续搜索下去时,就让搜索过程回溯(回退)到该节点的前一个结点,继续搜索该节点外的其他尚未搜索的分支;如果发现该结点无法再搜索下去,就让搜索过程回溯到这个结点的前一结点继续这样的搜索过程;这样的搜索过程一直进行到搜索到问题的解或者搜索完了全部可搜索分支没有解存在为止。

迷宫问题

首先我们需要有一张地图:
这里写图片描述
用0来标记此路不通,用1来标记此路可以走,后面会用2来标记走过的路。
我们默认出口和入口是在边界上的点,(出口与入口不是同一个)。
我们将走过的路的坐标压入栈中,如果能继续向下走,就一直入栈,直到走到出口。
如果下一个点不通,就将该点出栈。以此类推。
已上图为例,画一个栈来表示一下过程:
大致的出栈入栈过程如下图所示,灰色框表示该点由于不通或是已经被标记而已经被出栈。
这里写图片描述

递归实现

第一种方法,借助于函数递归调用时的栈帧结构,依赖于地址空间中的栈来辅助完成回溯法。
首先定义数据结构。

#include <stddef.h>
#define ROW 6
#define COL 6
typedef struct Maze
{
    size_t maze[ROW][COL];
}Maze;

typedef struct Point
{
    int x;
    int y;
}Point;

接下来,对地图初始化并且打印出来。

void MazeInit(Maze* m)
{
    if(m==NULL)
    {
        return;
    }
    int arr[ROW][COL]={
        {0,0,1,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,0,0,0,0}
    };
    int i=0;
    int j=0;
    for(i=0;i<ROW;i++)
    {
        for(j=0;j<COL;j++)
        {
            m->maze[i][j]=arr[i][j];
        }
    }
}
void MazePrint(Maze* m)
{
    if(m==NULL)
    {
        return;
    }
    int i=0;
    int j=0;
    for(i=0;i<ROW;i++)
    {
        for(j=0;j<COL;j++)
        {
            printf("%d ",m->maze[i][j]);
        }
        printf("\n");
    }
    printf("\n");
}

这里写图片描述

然后,需要搜索路径:

void _GetPath(Maze* m,Point entry,Point cur)
{
    printf("cur:(%d,%d)\n",cur.x,cur.y);
    //每次走到下一个点,都要调用此函数
    //1.判断当前点能否能落脚
    if(!CanStay(m,cur))
    {
        return;
    }
    //2.若能落脚,给当前位置坐标记
    Mark(m,cur);
    //3.若当前点为出口,说明找到了一条出路,探测就结束
    if(Exit(m,cur,entry))
    {
        printf("找到了一条出路\n");
        return;
    }
    //4。若当前点不是出口,按顺时针顺序探测四个方向的相邻点

    Point up=cur;
    up.x-=1;
    _GetPath(m,entry,up);

    Point right=cur;
    right.y+=1;
    _GetPath(m,entry,right);

    Point down=cur;
    down.x+=1;
    _GetPath(m,entry,down);

    Point left=cur;
    left.y-=1;
    _GetPath(m,entry,left);
}

void GetPath(Maze* m,Point entry)
{
    if(m==NULL)
    {
        return;
    }
    _GetPath(m,entry,entry);
}

辅助判断函数如下:

void Mark(Maze* m,Point cur)
{
    //走过的地方标记为2
    m->maze[cur.x][cur.y]=2;

}
int Exit(Maze* m,Point pt,Point entry)
{
    (void)m;
    //1当前点是不是入口,若为入口,就不是出口
    if(pt.x==entry.x&&pt.y==entry.y)
    {
        return 0;
    }
    //2.如果当前点在边界上,就是出口
    if(pt.x==0||pt.y==0||pt.x== ROW-1||pt.y==COL-1)
    {
        return 1;
    }
    return 0;
}
int CanStay(Maze* m,Point pt)
{
    //判断pt该点是否能落脚
    //若该点在地图外,则不能落脚
    if(pt.x<0||pt.x>=ROW||pt.y<0||pt.y>=COL)
    {
        return 0;
    }
    //若该点在地图内,位置为1,就能落脚,为0或2都不能落脚
    int value=m->maze[pt.x][pt.y];
    if(value==1)
    {
        return 1;
    }
    return 0;
}

结果如下:
将每次压入栈的点坐标也打印出来。
这里写图片描述
可以看到再找到出路之后,还是会回溯的遍历,直到将所有能走的点都标记过,才停止。

下图可以看到,确实将每个能走的点都标记了。
这里写代码片

手动定义一个栈实现

与上述思路是一样的,只是这里我们不采用函数调用时的栈帧,而是自己手动维护一个栈结构。
对于该栈结构,入栈表示保存当前点的坐标。出栈表示该点不能走了,必须要进行回溯。
代码如下:代码中有详细的注释

void GetPathByLoop(Maze* m,Point entry)
{
    //1.创建一个栈,并且初始化
    SeqStack stack;
    SeqStackInit(&stack);
    //2.判定入口能不能落脚,如果不能,说明参数非法
    if(!CanStay(m,entry))
    {
        return;
    }
    //3.标记入口点,将入口点入栈
    Mark(m,entry);
    SeqStackPush(&stack,entry);
    //4.进入循环,获取到当前的栈顶元素(栈顶元素一定能落脚)
    Point cur;
    while(SeqStackTop(&stack,&cur))
    {
        //5.判定这个点是不是出口,如果是出口,直接函数返回。
        //6.按照顺时针方向取相邻点,判定相邻点是否能够落脚,如果能落脚就标记入栈,立刻进入下一层循环。
        //7.如果四个相邻点都不能落脚,就出栈当前点回溯
        if(Exit(m,cur,entry))
        {
            printf("找到了一条路径\n");
            return;
        }
        Point up=cur;
        up.x-=1;
        if(CanStay(m,up))
        {
            Mark(m,up);
            SeqStackPush(&stack,up);
            continue;
        }

        Point right=cur;
        right.y+=1;
        if(CanStay(m,right))
        {
            Mark(m,right);
            SeqStackPush(&stack,right);
            continue;
        }

        Point down=cur;
        down.x+=1;
        if(CanStay(m,down))
        {
            Mark(m,down);
            SeqStackPush(&stack,down);
            continue;
        }

        Point left=cur;
        left.y-=1;
        if(CanStay(m,left))
        {
            Mark(m,left);
            SeqStackPush(&stack,left);
            continue;
        }

        SeqStackPop(&stack);
    }
}

结果如下所示:

这里写图片描述

【数据结构】用回溯法求解迷宫问题

今天呢,让我们来用栈求解一下数据结构中的著名问题---迷宫问题 我们先“制造”一个迷宫,把它放在Maze.txt文件中 Maze.txt 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ...
  • qq_31828515
  • qq_31828515
  • 2016-11-30 11:09:07
  • 3105

迷宫问题的回溯法求解的c++实现

  • 2009年11月07日 02:24
  • 4KB
  • 下载

迷宫问题——回溯法解

题目描述    迷宫是一个二维矩阵,其中1为墙,0为路,入口在第一列,出口在最后一列。     要求从入口开始,从出口结束,按照 上,下,左,右 的顺序来搜索路径...
  • zhuofeilong
  • zhuofeilong
  • 2015-08-31 10:53:14
  • 5341

回溯法解决迷宫问题(方法1---递归)

一、解决思路 1.创建迷宫,用0表示无障碍位置,1表示墙壁,比如迷宫m*p表示m行、p列,用二维数组Maze[m][p].但为了边缘处能够方便处理,在迷宫外层加一层几乎都为1的墙壁,除了进口和入口处设...
  • qq1169091731
  • qq1169091731
  • 2016-04-11 20:38:15
  • 487

迷宫问题(MazePath)的求解——利用回溯法(backtracking)

迷宫问题(MazePath)的求解——利用回溯法(backtracking) 1. 迷宫问题的提法 迷宫问题是典型的图的搜索问题。 假设一个迷宫,只有一个入口和一个出口。如果从迷宫的入口到达出口,...
  • cainv89
  • cainv89
  • 2016-05-25 23:07:51
  • 7509

回溯法求解迷宫问题

利用回溯法求解迷宫问题大致可以这样理解: 1.确定迷宫大小,确定迷宫格可走阻塞已走过的标志,确定入口出口; 2.想清楚如何走,如何找到出口(走到每一个格子都考虑其上下左右相邻能否走通,根据格子状态...
  • return_cc
  • return_cc
  • 2017-07-24 13:33:37
  • 175

回溯法解决迷宫问题

现在有迷宫地图:(回溯法)1 1 1 1 1 1 1 1 1 11 1 1 1 1 1 1 1 1 10 0 0 1 1 1 1 1 1 11 1 0 1 1 1 1 1 1 11 1 0 1 1 1...
  • qingqiulengya
  • qingqiulengya
  • 2016-05-30 10:46:59
  • 667

回溯算法解决迷宫问题

迷宫问题,首先解决的是两个问题:1、什么时候可以继续走也就是什么时候可以走?--a、不出界限、没有到限制条件,它可以继续运行。---b、当没有障碍物时候就继续运行:这里也会有问题,我走到这里时候那么我...
  • qq_33405276
  • qq_33405276
  • 2017-08-24 23:12:01
  • 295

VC++2012编程演练数据结构《8》回溯法解决迷宫问题

  • 2012年11月19日 19:03
  • 4KB
  • 下载

数据结构学习之回溯法求解迷宫问题

#include #include using namespace std; // 把迷宫表示为n个有编码路口的集合 // 定义路口类 class Crossing { publ...
  • earbao
  • earbao
  • 2012-11-01 00:12:05
  • 983
收藏助手
不良信息举报
您举报文章:数据结构-回溯法解决迷宫问题
举报原因:
原因补充:

(最多只允许输入30个字)