A*算法的目的是找到一條從起始狀態到最終狀態的最短路徑。算法
在A*算法中,須要在每一個點計算啓發函數:f(S) = g(S) + h(S),其中g(S)是從起點到S點的距離,h(S)是對從S點到終點的最短距離的估計值。數據結構
若是把g(S)看成深度,又令h(S) = 0,則A*算法就變成了BFS。函數
A*算法對BFS的改進在於把BFS所用到的隊列改爲按啓發函數值排列的優先隊列。spa
假設從狀態S抵達終點的最短距離爲H(S),那麼啓發函數中的h(S)必須知足h(S)<= H(S)。在此基礎上,h和實際的距離越接近,須要計算的節點就越少,效果就越好。若是h(S) = 0,此時A*算法就變成了BFS,效果最差。設計
在解8數字謎題時,若是按照BFS的思路,咱們每次擴展四個節點,分別是空格上移、下移、左移和右移(固然,大多數節點是沒法徹底擴展這四個方向的),一層層搜索以後,若發現了目標狀態,則從目標狀態節點開始向父節點回溯,由此就能夠獲得一個步數最少的解法了。可是BFS是盲目的,所以在搜索過程當中它搜索了不少「意義不大」的節點。因而咱們開始設計一個加入了必定啓發信息的A*算法(其思路相似於分支限界),代碼以下(參見P223 2.5.4.6小節):blog
A*() { // open表,用優先隊列實現,用來保存待擴展的節點(改進自BFS中的隊列)。 PriorityQueue open; // closed表,用HASH表或者其餘可以高效檢索的數據結構實現,用來保存已經擴展過的節點(能夠考察一個狀態是否已經被產生過)。 SymbolTable closed; open.Add(起始節點); while(!PriorityQueueEmpty(open)) { // 從open表中彈出一個最優的待擴展節點 S = PriorityQueueDeleteMax(open)); closed.Insert(S); // 若是S就是目標狀態則結束 if(S == GOAL) { return OVER; } // 嘗試擴展S的全部子節點 while(childS = S.nextChild()) { // 若是當前節點還沒有被產生過 if(!closed.isInclude(childS)) { // 則將這個節點添加到open表中 PriorityQueueInsert(open,childS); } } } }