问题描述
迷宫:一个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);
}