最近准备参加海康威视的一个软件挑战赛(http://codechallenge.hikvision.com/topic_introd.aspx?k1=6),需要用到路径搜索的算法,参考了网上的一些案例,自己实现了一个简单的A*算法。
A*算法是一种启发式的路径搜索算法。对于地图中的每一个节点,我们记录起点到该节点的消耗g,估算该节点到终点的消耗h(并不是准确值,有多种估算方法,简单的比如欧氏距离),已经两者之和f=g+h。
具体步骤为:
①将起点放入OpenList;
②从OpenList中选取f值最小的节点,记为V;
③将节点V从OpenList中移除,加入CloseList中;
④遍历节点V周围的节点,记为k,判断k其是否已经加入了CloseList:
k没有加入CloseList:
k是否加入了OpenList:
是:如果其通过节点V距离更近(即V.g+distance(V,k) < k.g),记录k的父节点为V
否:将k加入OpenList,设置其父节点为V
k加入了CloseList:
无操作
⑤重复②到④,知道遇到终点;
⑥从终点寻找其父节点,直到起点,得到了终点到起点的路径。
A*算法的具体流程可以参考这些博客,讲的非常详细:
http://www.cnblogs.com/technology/archive/2011/05/26/2058842.html
http://blog.csdn.net/b2b160/article/details/4057781
其流程为
把起始格添加到 "开启列表" do { 寻找开启列表中F值最低的格子, 我们称它为当前格. 把它切换到关闭列表. 对当前格相邻的8格中的每一个 if (它不可通过 || 已经在 "关闭列表" 中) { 什么也不做. } if (它不在开启列表中) 把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH if (它已经在开启列表中) { if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径) { 把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值. } } } while( 目标格已经在 "开启列表", 这时候路径被找到) 如果开启列表已经空了, 说明路径不存在. 最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.
以下给出具体实现的代码(这部分代码没有经过优化,运行很慢,但是能正确实现A*算法的功能):其中,"MapUtils.h"中定义了:二位数组ParkMap表示地图,iMapLength表示地图长度,iMapWidth表示地图宽度。
AStarUtils.h
// by 2017年5月27日20:19:49 #ifndef _A_STAR_UTILS_H_ #define _A_STAR_UTILS_H_ #include <vector> struct Point { Point() { x = 0; y = 0; } Point(int _x, int _y) :x(_x), y(_y) {} int x; // 点的横坐标,从0开始 int y; // 点的纵坐标,从0开始 }; struct ListNode { ListNode() { ListNode(Point(0, 0), 0, 0, 0); } ListNode(Point p, int f, int g, int h) :position(p), F(f), G(g), H(h), parentPosition(0, 0), hasParent(false) {} Point position; int F = 0; // F = G + H int G = 0; // 起点到该店的移动代价(已经走过的路程) int H = 0; // 该点到终点的估算成本(直接去横纵坐标差之和) Point parentPosition; bool hasParent; }; class AStar{ private: std::vector<ListNode*> openList; std::vector<ListNode*> closeList; public: // 根据输入的坐标,得到从入口到该坐标的路径 // start_x : 出发点的横坐标 // start_y : 出发点的纵坐标 // dest_x : 目标点的横坐标 // dest_y : 目标点的纵坐标 // succeed : 是否成功找到路径 // 返回 : 查找到的路径,但是不包括起始点 // 如起点为(0,0),终点为(0,1),返回路径中只会包含(0,1)。 std::vector<Point> findPathInMap(int start_x, int start_y, int dest_x, int dest_y, bool & succeed); // 查找openlist中F值最小的节点 ListNode * findLeastFInOpenList(); // 检查一个节点是否在list中 bool isNodeInOpenList(ListNode * node); bool isNodeInCloseList(ListNode * node); // 根据节点的坐标,找到对应的节点 ListNode * findNodeInOpenList(Point p); ListNode * findNodeInCloseList(Point p); // 根据节点的坐标,从list中移除节点 void removeNodeFromOpenList(Point p); void removeNodeFromCloseList(Point p); // 根据输入的坐标,得到从入口到该坐标的路径 // dest_x : 目标点的横坐标 // dest_y : 目标点的纵坐标 // succeed : 是否成功找到路径 // 返回 : 查找到的路径,但是不包括起始点 // 如起点为(0,0),终点为(0,1),返回路径中只会包含(0,1)。 std::vector<Point> findPathFromEntrance(int dest_x, int dest_y, bool & succeed); // 根据输入的坐标,得到该坐标到出口的路径 // start_x : 出发点的横坐标 // dest_y : 出发点的纵坐标 // succeed : 是否成功找到路径 // 返回 : 查找到的路径,但是不包括起始点 // 如起点为(0,0),终点为(0,1),返回路径中只会包含(0,1)。 std::vector<Point> findPathToExpot(int start_x, int dest_y, bool & succeed); }; #endif
AStarUtils.cpp// by denghaijin 2017年5月27日20:20:26 #include "AStarUtils.h" #include "MapUtils.h" std::vector<Point> AStar::findPathInMap(int start_x, int start_y, int dest_x, int dest_y, bool & succeed) { for (int i = 0; i < openList.size(); i++) delete openList.at(i); openList.clear(); for (int i = 0; i < closeList.size(); i++) delete closeList.at(i); closeList.clear(); // 1 将起点加入open list int start_g = 0; int start_h = abs(start_x - dest_x) + abs(start_y - dest_y); int start_f = start_g + start_h; openList.push_back(new ListNode(Point(start_x, start_y), start_f, start_g, start_h)); // 2 遍历 bool stop = false; while (!stop) { // 遍历 open list ,查找 F 值最小的节点,把它作为当前要处理的节点 ListNode * node_min_f = findLeastFInOpenList(); if (node_min_f == NULL) break; // 把这个节点移到 close list ListNode * tmp_node = new ListNode(node_min_f->position, node_min_f->F, node_min_f->G, node_min_f->H); tmp_node->parentPosition = node_min_f->parentPosition; tmp_node->hasParent = node_min_f->hasParent; closeList.push_back(tmp_node); removeNodeFromOpenList(node_min_f->position); // 对当前方格的四周方格进行如下操作 // 左边的点 if (node_min_f->position.x > 0) { int cur_x = node_min_f->position.x - 1; int cur_y = node_min_f->position.y; if (cur_x == dest_x && cur_y == dest_y) // 到达终点 { int g = node_min_f->G + 1; int h = 0 + 0; int f = g + h; ListNode * destNode = new ListNode(Point(cur_x, cur_y), f, g, h); destNode->parentPosition = node_min_f->position; destNode->hasParent = true; closeList.push_back(destNode); stop = true; break; } if (ParkMap[cur_y][cur_x].Mark == *"X") { int g = node_min_f->G + 1; int h = abs(cur_x - dest_x) + abs(cur_y - dest_y); int f = g + h; ListNode * leftNode = new ListNode(Point(cur_x, cur_y), f, g, h); if (!isNodeInCloseList(leftNode)) { if (!isNodeInOpenList(leftNode)) { leftNode->parentPosition = node_min_f->position; leftNode->hasParent = true; openList.push_back(leftNode); } else { ListNode * oldNode = findNodeInOpenList(leftNode->position); if (leftNode->G < oldNode->G) { oldNode->parentPosition = node_min_f->position; oldNode->G = leftNode->G; oldNode->H = leftNode->H; oldNode->F = leftNode->F; oldNode->hasParent = true; } } } } } // 右边的点 if (node_min_f->position.x < iMapLength - 1) { int cur_x = node_min_f->position.x + 1; int cur_y = node_min_f->position.y; if (cur_x == dest_x && cur_y == dest_y) // 到达终点 { int g = node_min_f->G + 1; int h = 0 + 0; int f = g + h; ListNode * destNode = new ListNode(Point(cur_x, cur_y), f, g, h); destNode->parentPosition = node_min_f->position; destNode->hasParent = true; closeList.push_back(destNode); stop = true; break; } if (ParkMap[cur_y][cur_x].Mark == *"X") { int g = node_min_f->G + 1; int h = abs(cur_x - dest_x) + abs(cur_y - dest_y); int f = g + h; ListNode * rightNode = new ListNode(Point(cur_x, cur_y), f, g, h); if (!isNodeInCloseList(rightNode)) { if (!isNodeInOpenList(rightNode)) { rightNode->parentPosition = node_min_f->position; rightNode->hasParent = true; openList.push_back(rightNode); } else { ListNode * oldNode = findNodeInOpenList(rightNode->position); if (rightNode->G < oldNode->G) { oldNode->parentPosition = node_min_f->position; oldNode->G = rightNode->G; oldNode->H = rightNode->H; oldNode->F = rightNode->F; oldNode->hasParent = true; } } } } } // 上边的点 if (node_min_f->position.y > 0) { int cur_x = node_min_f->position.x; int cur_y = node_min_f->position.y - 1; if (cur_x == dest_x && cur_y == dest_y) // 到达终点 { int g = node_min_f->G + 1; int h = 0 + 0; int f = g + h; ListNode * destNode = new ListNode(Point(cur_x, cur_y), f, g, h); destNode->parentPosition = node_min_f->position; destNode->hasParent = true; closeList.push_back(destNode); stop = true; break; } if (ParkMap[cur_y][cur_x].Mark == *"X") { int g = node_min_f->G + 1; int h = abs(cur_x - dest_x) + abs(cur_y - dest_y); int f = g + h; ListNode * upNode = new ListNode(Point(cur_x, cur_y), f, g, h); if (!isNodeInCloseList(upNode)) { if (!isNodeInOpenList(upNode)) { upNode->parentPosition = node_min_f->position; upNode->hasParent = true; openList.push_back(upNode); } else { ListNode * oldNode = findNodeInOpenList(upNode->position); if (upNode->G < oldNode->G) { oldNode->parentPosition = node_min_f->position; oldNode->G = upNode->G; oldNode->H = upNode->H; oldNode->F = upNode->F; oldNode->hasParent = true; } } } } } // 下边的点 if (node_min_f->position.y < iMapWidth - 1) { int cur_x = node_min_f->position.x; int cur_y = node_min_f->position.y + 1; if (cur_x == dest_x && cur_y == dest_y) // 到达终点 { int g = node_min_f->G + 1; int h = 0 + 0; int f = g + h; ListNode * destNode = new ListNode(Point(cur_x, cur_y), f, g, h); destNode->parentPosition = node_min_f->position; destNode->hasParent = true; closeList.push_back(destNode); stop = true; break; } if (ParkMap[cur_y][cur_x].Mark == *"X") { int g = node_min_f->G + 1; int h = abs(cur_x - dest_x) + abs(cur_y - dest_y); int f = g + h; ListNode * downNode = new ListNode(Point(cur_x, cur_y), f, g, h); if (!isNodeInCloseList(downNode)) { if (!isNodeInOpenList(downNode)) { downNode->parentPosition = node_min_f->position; downNode->hasParent = true; openList.push_back(downNode); } else { ListNode * oldNode = findNodeInOpenList(downNode->position); if (downNode->G < oldNode->G) { oldNode->parentPosition = node_min_f->position; oldNode->G = downNode->G; oldNode->H = downNode->H; oldNode->F = downNode->F; oldNode->hasParent = true; } } } } } //判断是否结束 // 终点加入openlist //if (findNodeInOpenList(Point(dest_x, dest_y)) != NULL) // stop = true; // 查找终点失败,并且 open list 是空的,此时没有路径 // ... } // temp std::vector<Point> reverseList; ListNode * curNode = findNodeInCloseList(Point(dest_x, dest_y)); while (curNode != NULL && curNode->position.x != start_x || curNode->position.y != start_y) { reverseList.push_back(curNode->position); curNode = findNodeInCloseList(curNode->parentPosition); } std::vector<Point> list; for (int index = reverseList.size() - 1; index >= 0; index--) { list.push_back(reverseList.at(index)); } return list; } ListNode * AStar::findLeastFInOpenList() { ListNode * res_node = NULL; int minF = INT_MAX; std::vector<ListNode*>::iterator ite; for (ite = openList.begin(); ite != openList.end(); ite++) { ListNode * pNode = *ite; if (pNode->F < minF) { res_node = pNode; minF = pNode->F; } } return res_node; } bool AStar::isNodeInOpenList(ListNode * node) { std::vector<ListNode*>::iterator ite; for (ite = openList.begin(); ite != openList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == node->position.x && pNode->position.y == node->position.y) { return true; } } return false; } bool AStar::isNodeInCloseList(ListNode * node) { std::vector<ListNode*>::iterator ite; for (ite = closeList.begin(); ite != closeList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == node->position.x && pNode->position.y == node->position.y) { return true; } } return false; } ListNode * AStar::findNodeInOpenList(Point p) { std::vector<ListNode*>::iterator ite; for (ite = openList.begin(); ite != openList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == p.x && pNode->position.y == p.y) { return pNode; } } return NULL; } ListNode * AStar::findNodeInCloseList(Point p) { std::vector<ListNode*>::iterator ite; for (ite = closeList.begin(); ite != closeList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == p.x && pNode->position.y == p.y) { return pNode; } } return NULL; } void AStar::removeNodeFromOpenList(Point p) { std::vector<ListNode*>::iterator ite; for (ite = openList.begin(); ite != openList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == p.x && pNode->position.y == p.y) { openList.erase(ite); break; } } } void AStar::removeNodeFromCloseList(Point p) { std::vector<ListNode*>::iterator ite; for (ite = closeList.begin(); ite != closeList.end(); ite++) { ListNode * pNode = *ite; if (pNode->position.x == p.x && pNode->position.y == p.y) { closeList.erase(ite); break; } } }