关于A*算法,有些资料讲解很不错,如
https://blog.csdn.net/hitwhylz/article/details/23089415
https://www.jianshu.com/p/a3951ce7574d
在上篇滑块拼图的问题
https://blog.csdn.net/aaajj/article/details/100375751
处理后,我发现,从广度优先搜索(BFS)的角度去理解A*算法非常清晰易懂。
A*可以看成是对广度优先搜索的一种改进,在BFS中,使用队列保存每层中的节点,
从队列头部取出节点进行检查的时候,再将其子节点都放到队列的尾部,这样实现按照层次查找的效果。
A*对节点队列进行了处理,总是优先对估值低(最优)的节点进行处理,这有些类似动态规划,这样就避免了BFS搜索的盲目性。
体现出了“以正合,以奇胜”的哲学思想
使用一个表(队列)来存放待检查的节点
Open表可以使用一个有序链表结构来实现,主要有添加和删除操作,如
//test A*
void addNode(PNode* head, PNode* data)
{
while (head->next != NULL)
{
PNode* t = head->next;
//already in the list
if (t == data)
{
return;
}
if (data->f < t->f)
{
data->next = t;
head->next = data;
return;
}
head = head->next;
}
if (head->next == NULL)
{
head->next = data;
data->next = NULL;
}
}
void rmNode(PNode* head, PNode* data)
{
while (head->next != NULL)
{
PNode* t = head->next;
if (t == data)
{
head->next = t->next;
delete t;
return;
}
head = head->next;
}
}
Close表由于需要经常用来查找,可以使用map来实现
估值函数h一般要在一定程度上表现出各节点间距离的差异。
对于8数码以及滑块拼图问题,估值函数h可以简单的用不匹配的滑块数量来表示,如
目标:
1 2 3
4 5 6
7 8 0
状态A
1 2 3
4 0 5
7 8 6
的h值为3,有3个位置不匹配,A可以通过滑动0到达状态B和C
B:(h值为2)
1 2 3
4 5 0
7 8 6
C:(h值为4)
1 2 3
0 4 5
7 8 6
显然,从状态A进行移动选择的时候,选择h值为2的B状态更好,事实也确实如此。
h函数也可以稍微复杂点的绝对距离和来表示
以状态A为例
1 2 3
4 0 5
7 8 6
计算每个滑块分别单独放好的步数,对其求和来作为h估值
这里,h=h(1) +h(2)+h(3)+h(4)+h(5)+h(6)+h(7)+h(8)+h(0)
=0 + 0 +0 + 0 + 1+ 1 + 0 + 0+ 2
=4
我们也可以不把空白块0计算进去,这要看对h函数的具体设计。