A*算法是用於尋找兩點之間的最短路徑,同時它也是一種靜態路網中求解最短路最有效的直搜索方法,公式f(n)=h(n)+g(n)給出了鄰居節點到目標節點所需要的總消耗成本,h(n)是當前節點到該鄰居節點的所消耗的成本,g(n)是該鄰居節點到目標節點的估計消耗成本,比較常用的估計方法是歐幾里得方法和曼哈頓方法。
A*算法首先要准備兩個列表,一個開啟列表,一個關閉列表,開啟列表存儲還未走的節點(但不是一開始就把所有節點加入開啟列表),關閉列表存儲走過的節點,將起始節點加入到開啟列表,在開啟列表不為空時,取出開啟列表中最小消耗成本的節點,判斷是否是目標節點,是就直接結束A*,取得它的周圍鄰居節點(通常是8個),判斷鄰居節點是否在關閉列表中,在關閉列表中,不做處理,不在關閉列表中,再判斷是否在開啟列表中,若已在開啟列表中,計算它的f(n),是否比以前的f(n)小,小就更新它的f(n)並將它的父節點設為當前節點。大的話就不做處理,若不在開啟列表中,計算它的f(n),更新f(n)將父節點設為當前節點。處理完它的周圍鄰居節點后,一輪循環結束,再移除開啟列表中的當前節點,放在關閉列表中,因為已經走過這個節點。然后在開啟列表不為空時繼續上述循環,直到找到目標節點,或者找遍所有節點都找不到目標節點。C++代碼如下(相關函數並未實現,視實際情況而定):
1 typedef struct node 2 { 3 float currentCost;//h(n) 4 float toTalCost;//f(n) 5 node * parent;//父節點 6 }Node; 7 Vector<Node> OpenList;//開啟列表 8 Vector<Node> CloseList;//關閉列表 9 10 Node AStar(Node start,Node Goal){ 11 OpenList.Add(start); 12 while(OpenList.Length!=0) 13 { 14 Node* minNode= OpenLIst.Min();//Min()選出最小消耗成本節點 15 if(minNode == Goal) 16 { 17 return minNode; 18 } 19 Vector<Node> Neighbors;//鄰居列表 20 GetNeighBors(Neighbors,minNode);/*得到最小消耗成本節點的周圍鄰居節點*/ 21 for(int i=0;i<NeighBors.Count;i++) 22 { 23 if(Neighbors[i].obstacle)//如果鄰居節點被標志障礙物,跳過。 24 { 25 continue;/*也可以在GetNeighBors函數里進行處理,障礙物節點不加入鄰居列表*/ 26 } 27 if(CloseList.Contain(Neighbors[i]))//節點已經走過不作處理 28 continue; 29 float currentCost=CacluateDistance(minNode,Neighbors[i])+minNode.currentCost; 30 float totalCost = currentCost+CacluateDistance(Neighbors[i],Goal); 31 if(OpenList.Cotain(Neighbors[i]))//如果開啟列表包含該鄰居節點 32 { 33 34 if(totalCost<Neighbors[i].totalCost) 35 { 36 Neighbors[i].totalCost=totalCost; 37 Neighbors[i].currentCost =currentCost; 38 Neighbors[i].parent=minNode; 39 } 40 } 41 else 42 { 43 Neighbors[i].totalCost=totalCost; 44 Neighbors[i].currentCost =currentCost; 45 Neighbors[i].parent=minNode; 46 } 47 } 48 OpenList.Remove(minNode); 49 CloseList.Add(minNode); 50 } 51 return NULL;//找不到目標節點,返回空 52 }
若成功返回一個節點,那么可以不斷往回迭代父節點得到一條逆向的最短路徑。A*的思想是為每個節點都估價消耗成本(沿當前所走的路徑到目標節點的消耗成本),選擇最小消耗成本的節點優先計算,就使節點搜索沿目標節點那邊搜索,也被稱之為啟發式搜索。而開啟列表得到最小的消耗成本節點或添加節點,應該用二叉堆(最小堆)插入,這樣取最小節點時間復雜度為O(1),添加節點時間復雜度為O(log(n)),比另一種遍歷取節點O(n),加節點O(1)好一些。
