關於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函數的具體設計。