一、广度优先遍历和深度优先遍历
在学习寻路算法之前,我们先来了解一下广度优先遍历和深度优先遍历.
什么是广度优先遍历?
广度优先遍历(breadth first search)是一个万能的算法.
广度优先是从初始状态一层一层向下找,直到找到目标为止。
广度优先遍历,指的是从一个未遍历的节点出发,先遍历这个节点的相邻节点,再依次遍历每个相邻节点的相邻节点。
例如:

这里遍历的顺序就是 从1到10
广度优先遍历相当于一个队列
先遍历1节点, 相邻节点234加入队列
然后按照顺序开始遍历2节点, 相邻节点5加入队列
再遍历3节点,67节点加入队列
以此类推一直到遍历结束.
什么是深度优先遍历?
DFS顾名思义就是一条路走到黑,再寻其他未走过的路。
深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。
上面的遍历顺序就是
1,2,5,9,3,6,10,7,4,8
其实我们用递归算法遍历二叉树的时候,就是这种遍历方法,也叫先序遍历.
广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。这在状态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。他的效率实在太低,甚至不可完成。在这里就要用到启发式搜索了。
最简单的寻路
我们来看下,下面最简单的寻路方式是广度优先遍历还是深度优先遍历?
假设:
我们的人物在一个小地图中,每次只能朝上下左右四个方向行走一步.这样的方式走到目的地.
我们每次向4个方向探索,得到边界,下次再继续探索边界
直到探索到目的地为止。
探索的过程中顺势记录下方块的来向,这样在探索到目的地的时候,知道怎么走过来的.也就获得了寻路路径.



这个算法非常的简单。
保存个队列循环遍历就可以了.
很明显这是广度优先遍历,也是可以达到寻路的目的的,
但是这相当于穷举, 没有方向性, 所以效率非常慢!
尤其地图很大的时候,成几何倍数的影响效率.
如果你不在乎效率或则地图非常小,那么你看到这里就够了。
但是,如果我们需要一个更效率更智能的算法,
那就需要了解A星算法(A*算法)
A星不会像广度优先遍历那样探索所有的步骤,而是选择每一步中最优的方块。
这样就可以大大提高效率了。
那么怎么来选择每一步中最优的方块呢?
这里面就涉及到了一个代价值的概念。
字面意思从A点到B点,我们需要付出的代价嘛
如果A点到B点是1公里,A点到C点是2公里,完全没有其他因素干扰的情况下
我们付出的代价肯定是 AC的代价= AB的代价*2.
比如我们要走到红色方框

首先遍历出来和我们相邻的所有节点也就是格子。
然后我们要从遍历出来的4个格子中选择一个代价值最低的格子作为最优格子。
我们这里就暂时只用移动的距离当代价值。
实际情况中,我们可能还需要根据地形,是否和障碍物相邻等等情况判断不同的代价。
每个格子的代价值(F) = 起点到他的代价值 (G)+ 预估从当前格子到终点的代价(H)
为什么要预估呢?因为我们不知道啊,如果能准确知道的话不就知道寻路路线了吗?还计算什么。
所以我们现在只能在忽略地形忽略障碍物的情况下,估算一个代价值。
可以计算直线距离,或则直接横向和纵向距离和(曼哈顿距离)。
后者不需要开平方操作(sqrt)这样会提高一些效率。
以上四个格子,我们叫做上下左右
他们的G 都是 1,这毫无疑问
他们的H 值分别是 4,4,4,2
很明显 右边格子 由于接近终点,所以是代价值最低的。所以我们会选择他为最优节点继续遍历。
当然选择的最优节点不一定是全局的最优节点例如下图:

很明显,向右走是不对的。
但是这就是探索,只有探索到障碍物,发现行不通,才会回头找更优的节点.
我们后面会根据继续的探索来修正线路。不过此时右就是最优节点。
大家可以到以下地址
Introduction to the A* Algorithm
对比一下广度优先遍历和A*遍历的区别和速度。

了解了基本的遍历思想
我们来详细的学习一下
二、A*算法
算法的过程解析
我们还是拿一个小例子来一步步的学习整个算法的过程。
不要嫌繁琐,我们能跟着思路完全的走一遍,完全理解算法的流程,才能把他应用到更多的场景,才能根据自己的需求随意的修改,而不是只会抄袭算法.
1.初始状态,这是一个7*7的小地图,起始点坐标 (3,1 )终点坐标 (3,4)
我们进行寻路,中间做一堵墙.

首先,在正式开始前,我们先做一定的准备工作。
①,我们要有地图的信息,用一个二维数组来表示,0表示可以走的格子,1表示障碍物。
vector<vector<int>> maptemp =
{
{0,0,0,0,0,0,0},
{0,0,0,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,1,0,0,0,0},
{0,0,0,0,0,0,0}
};
②创建两个表
一个表叫做openlist 来存放所有正在探索的节点,每次从整个表中找到最优节点.
一个表叫做closelist 来存放所有已经探索过的最优节点。探索过的就不需要再探索了.
③节点的属性
节点应该有坐标x,y 代价值F,G,H 同时还要有父节点,这样在我们探索到终点的时候,就可以根据父节点回溯出整条寻路路径。什么是父节点? 这个节点从哪个节点走过来的,哪个节点就是他的父节点,起点没有父节点.
我们先简写成伪代码这样: {x,y,F,G,H,father}
好的那么开始:
起点的属性={3,1,3,0,3,0} 起点坐标3,1 G = 0 H = 3 F = G+H = 3 父节点是空指针。
初始状态,我们只有起点,那么将起点加入 openlist.
openlist = {(3,1,3,0,3,0)};
closelist = {}
2.

openlist中选择最优节点,很明显只有起点一个节点。
那么把最优节点(3,1)从openlist中删除 因为已经探索过了,同时加入到closelist中
找到最优节点周围可以走的方格,障碍物排除在外,并且加入openlist
openlist = {(3,0,5,1,4,(3,1))
(2,1,5,1,4,(3,1))
(4,1,5,1,4,(3,1))
};
closelist = {(3,1,3,0,3,0)}
此时3个格子的F值都是5 = 1(出发点达到这里的值) + 预算值4
3.

openlist中选择最优节点,3个节点F值一样。
所以会根据存入openlist的先后顺序,选择一个作为下一个最优节点,这里选择的(2,1)
图中是先存放的上,我们自己存放不一定哦.
那么把最优节点(2,1)从openlist中删除,同时加入到closelist中
找到最优节点周围可以走的方格,障碍物排除在外,已经在列表中的节点排除,并且加入openlist
openlist = {(3,0,5,1,4,(3,1))
(4,1,5,1,4,(3,1))
(2,0,7,2,5,(2,1))
(1,1,7,2,5,(2,1))
};
closelist = {(3,1,3,0,3,0)
(2,1,5,1,4,(3,1))
}
此时新加入的2个格子的F值都是7 明显比最开始的2个格子的F值大,所以这里下一步不会沿着当前线路继续遍历,而是会回头去判断之前的线路有没有更优解。
4.

openlist中选择最优节点,这里选择的(3,0)
那么把最优节点(3,0)从openlist中删除,同时加入到closelist中
找到最优节点周围可以走的方格,障碍物排除在外,已经在列表中的节点排除,并且加入openlist
(((但是这里值得注意的是:
如果周围相邻的节点,
已经在closelist列表中,说明再走向他就是回头路了,所以不用考虑.
已经在openlist列表中的节点,还在探索中,那么之前的F值是通过之前的其他最优节点计算出来的.
如果通过我们现在的最优节点计算出来的F值比之前的小,那么应该替换!
比如这里 2,0 就是 之前的 F 值 = 2 + 5
现在的F 值 = 2 + 5 相等所以不用替换.)))
openlist = {
(4,1,5,1,4,(3,1))
(2,0,7,2,5,(2,1))
(1,1,7,2,5,(2,1))
(4,0,7,2,5,(3,0))
};
closelist = {(3,1,3,0,3,0)
(2,1,5,1,4,(3,1))
(3,0,5,1,4,(3,1))
}
5.

openlist中选择最优节点,那么毫无疑问,这次应该选择 最后一个代价值5的节点了
那么把最优节点(4,1)从openlist中删除,同时加入到closelist中
找到最优节点周围可以走的方格,障碍物排除在外,已经在列表中的节点排除,并且加入openlist
openlist = {
(2,0,7,2,5,(2,1))
(1,1,7,2,5,(2,1))
(4,0,7,2,5,(3,0))
(5,1,7,2,5,(4,1))
};
closelist = {(3,1,3,0,3,0)
(2,1,5,1,4,(3,1))
(3,0,5,1,4,(3,1))
(4,1,5,1,4,(3,1))
}
目前openlist中就是4个代价值为7的节点了.
目前从起始开始到这里.由于地形的限制,此时我们的A*遍历和正常的广度优先遍历得到的结果和效率没什么区别,那么一会找到一个更优的节点就出现很大的差别了。
这里我们也能看出来了, 边缘的节点就是openlist中的节点, 而内部已经探索过的节点就是closelist节点
6.

继续探索
openlist中选择最优节点 2,0
那么把最优节点(2,0)从openlist中删除,同时加入到closelist中
找到最优节点周围可以走的方格,障碍物排除在外,已经在列表中的节点排除,并且加入openlist
openlist = {
(1,1,7,2,5,(2,1))

本文详细介绍A*寻路算法的原理与实现过程,并对比广度优先遍历,阐述算法优势及应用场景。
最低0.47元/天 解锁文章
1万+





