早前寫了一篇關於A*算法的文章:《算法:Astar尋路算法改進》
最近在寫個js的UI框架,順便實現了一個js版本的A*算法,與之前不同的是,該A*算法是個雙向A*。
雙向A*有什么好處呢?
我們知道,A*的時間復雜度是和節點數量以及起始點難度呈冪函數正相關的。
這個http://qiao.github.io/PathFinding.js/visual/該網址很好的演示了雙向A*的效果,我們來看一看。
綠色表示起點,紅色表示終點,灰色是牆面。稍淺的兩種綠色分別代表open節點和close節點:
當路徑通過狹窄通道時,如果起點離通道較近,則很容易找到了終點,把起點和終點交換一下,如圖:
可以注意到,被查找的節點多了不止一倍。
我們可以認為,終點在狹窄地帶是最差結果,在開闊地帶是最優結果。再來看看雙向A*算法的尋址:
可以看到,和單向的最優結果是很接近的。
觀察淺綠色尋路節點我們可以注意到,當open節點第一次接觸到之后,中止了查找。
根據該思路,實現算法如下:
while (true) { minFNode = anra.AStarUtil.findMinNode(openList); openList.removeObject(minFNode); if (!closedList.contains(minFNode)) closedList.push(minFNode); if (closedList.length > 500) { return; } if (minFNode == null || minFNode.equals(endNode)) break; anra.AStarUtil.search(this, minFNode, openList, closedList, endNode); BIminFNode = anra.AStarUtil.findMinNode(BIopenList); BIopenList.removeObject(BIminFNode); if (!BIclosedList.contains(BIminFNode)) BIclosedList.push(BIminFNode); if (BIclosedList.length > 500) { return; } if (BIminFNode == null || BIminFNode.equals(startNode)) break; anra.AStarUtil.search(this, BIminFNode, BIopenList, BIclosedList, startNode); for (var i = 0; i < openList.length; i++) { for (var j = 0; j < BIopenList.length; j++) { if (BIopenList[j].equals(openList[i])) { BIminFNode = BIopenList[j]; minFNode = openList[i]; middleNode = minFNode; break; } } if (middleNode) break; } if (middleNode) break; }
findMinNode:function (openList) { if (openList.length == 0) return null; else if (openList.length == 1) return openList[0]; openList.sort(function (a, b) { return a.f() - b.f(); }); return openList[0]; }, /*搜索*/ search:function (router, node, openList, closedList, endNode) { var nodes = this.findAroundNode(router, node); if (nodes == null) return; for (var i = 0; i < 8; i++) { if (nodes[i] == null || nodes[i].level == null)continue; nodes[i].g = (i > 3 ? nodes[i].level[0] : nodes[i].level[1]) + node.g; nodes[i].h = this.caculateH(nodes[i], endNode); if (closedList.contains(nodes[i])) { continue; } if (!openList.contains(nodes[i])) { openList.push(nodes[i]); nodes[i].parent = node; } else { var idx = openList.indexOf(nodes[i]); var n = openList[idx]; if (nodes[i].g < n.g) { openList.remove(idx); closedList.push(n); nodes[i].parent = n.parent; openList.splice(idx, 0, nodes[i]); } } } }, /*查找指定節點周圍的可用節點*/ findAroundNode:function (router, node) { if (node == null)return null; var nodes = []; nodes[0] = ANodeFactory.create(router, node.i, node.j + 1); nodes[1] = ANodeFactory.create(router, node.i, node.j - 1); nodes[2] = ANodeFactory.create(router, node.i + 1, node.j); nodes[3] = ANodeFactory.create(router, node.i - 1, node.j); nodes[4] = ANodeFactory.create(router, node.i - 1, node.j + 1); nodes[5] = ANodeFactory.create(router, node.i + 1, node.j - 1); nodes[6] = ANodeFactory.create(router, node.i + 1, node.j + 1); nodes[7] = ANodeFactory.create(router, node.i - 1, node.j - 1); return nodes; }, caculateH:function (p, endNode) { return (Math.abs(endNode.i - p.i) + Math.abs(endNode.j - p.j)) * p.level[0]; },