GIS系统中最短路径查找算法优化之一:双向广度优先搜索

     去年毕业的时候进入一家GIS软件开发公司,正式接手的第一个项目是GIS系统的拓扑分析,在公司原有的算法基础上进行维护和优化。在优化过程有了以下心得,本该去年就更新在blog上的,但是一直没有时间去弄,现更新之。

     在GIS系统中,最常用的一个功能就是在拓扑网络结构中进行最短路径分析。

    

     注:最短路径分析公司取名为FindAPath.

 

     公司前辈最初使用的算法是广度优先算法。因为广搜一旦搜索到解即为最小的步数,这是深搜所忘尘莫及的。

     代码流程大致如下:

     list MiddleNodes; //用于保存待搜索子节点 list::iterator listIter; map PreMap; //用于保存搜索路径 map::iterator mapIter; set SearchNodes; //已搜索过的节点eid,防止环路,死循环 set::iterator setIter; MiddleNodes.push_back(lBeginNode);//初始化分析起点 PreMap[lBeginNode] = -1;//用于在回溯路径的时候中止条件 while(!MiddleNodes.empty()) { listIter = MiddleNodes.begin(); MiddleNodes.erase(listIter); //取要搜索的,在队列中删除之 SearchNodes.insert(*listIter); //将搜索过的节点记录下来 //获取到待分析点的所有子节点,函数设计业务不列出 list lChildNods = GetChildNodes(*ListIter); list::iterator tempIter(lChildNods.begin()); list::iterator EndIter(lChildNods.end()); while (tempIter != EndIter) { setIter = SearchNodes.find(*tempIter); //防止回环 if (setIter == SearchNodes.end()) { PreMap[*setIter] = *tempIter;//记录每条路径 MiddleNodes.push_back(*setIter);//将子节点插入到待分析队列 } ++tempIter; } } list ListResult; //保存结果路径 long lTempValue = -1; //遍历路径 while (1) { mapIter = PreMap.find(lEndNode); lTempValue = (*mapIter).second; if(lTempValue==-1) break; else { ListResult.push_front(lTempValue); } }

 

     以上代码大致是我凭记忆写下来的,之前的代码备份地址暂时没找到。优化过程中我选择使用双向广度优先搜索算法。

     同样的逻辑不同的是同时从起点和终点向中间搜索遇到交叉点中止。论坛很多人认为双广其实没有什么提高,这样看业务层导致数据层的依赖关系,在我的GIS项目中,一个点设备大部分都有4个子节点,那么广搜层数越大,子节点数成字数增长。在我的业务系统中,双搜层数总会比单搜层数少。比如单搜需要4层找到,那么经过的节点数会在4的4次方即256个,双搜一般会两个层中止即2的4次方乘以2为32.后面将列出我的测试数据。但是极端情况下双搜可能适得其反,例如起点和终点完全没有连通性的时候,搜索个数会加倍。

    以下是我的代码:

    long NodeNo = 0; long LineNo = 0; long middlePoint = 0; long middleLine = 0; long DataCnt = 0; bool bForceBreak = false; //若找到终止点,强制退出while循环 long valOfBegin = 0, parentOfBegin = 0; long valOfEnd = 0, parentOfEnd = 0; vector pData; list MiddleNodesOfBegin; list::iterator listIterOfBegin; map PreMapOfBegin; map::iterator mapIterOfBegin; list MiddleNodesOfEnd; list::iterator listIterOfEnd; map PreMapOfEnd; map::iterator mapIterOfEnd; //如果起点和终点为同一个点。 if(BeginEid == EndEid) { Nodes.push_back (BeginEid); return 1; } //1222--modify by buffer--使用同时从起点和终点搜索路径的算法 MiddleNodesOfBegin.push_back(BeginEid); MiddleNodesOfEnd.push_back(EndEid); PreMapOfBegin[BeginEid] = -2; PreMapOfEnd[EndEid] = -2; while((!MiddleNodesOfBegin.empty()) && (!MiddleNodesOfEnd.empty())) { //从前端查找一次 valOfBegin = MiddleNodesOfBegin.front(); MiddleNodesOfBegin.pop_front(); mapIterOfBegin = PreMapOfBegin.find(valOfBegin); parentOfBegin = (*mapIterOfBegin).second; GetAdjacentDataByEid(valOfBegin, versionNo, pData); DataCnt = pData.size()/2; if(DataCnt <= 0) { continue; } for(long i=0; idebug("SearchNodes from the begin--->%ld", PreMapOfBegin.size()/2); //logger->debug("SearchNodes from the end--->%ld", PreMapOfEnd.size()/2); if(!bForceBreak) { return -1; //两点间不连通 } //得出起点开始的路径 long tmpPoint = middlePoint; while(-1 != middlePoint) { Nodes.push_front(middlePoint); NodeNo++; mapIterOfBegin = PreMapOfBegin.find(middlePoint); middleLine = (*mapIterOfBegin).second; if (-2 == middleLine) { break; } Lines.push_front(middleLine); LineNo++; mapIterOfBegin = PreMapOfBegin.find(middleLine); middlePoint = (*mapIterOfBegin).second; } //将终点搜索到的路径和从起点搜索到的路径合并 mapIterOfEnd = PreMapOfEnd.find(tmpPoint); middleLine = (*mapIterOfEnd).second; while (-2 != middleLine) { Lines.push_back(middleLine); LineNo++; mapIterOfEnd = PreMapOfEnd.find(middleLine); middlePoint = (*mapIterOfEnd).second; Nodes.push_back(middlePoint); NodeNo++; mapIterOfEnd = PreMapOfEnd.find(middlePoint); middleLine = (*mapIterOfEnd).second; }

 

     以下是优化的测试报告。修改算法从两个断点开始同时向中间搜索经过测试的结果是: 1.在两点之间有联通关系的情况下 输入参数为:FindAPath;V_T_PD_SB_GSGLKG_GEO;54;V_T_PD_SB_DL_GEO;2000;0 修改算法前的效率为: 时间为: 13359.00ms 中间走过的节点为: 81070个点 修改算法后的效率为: 时间为: 844.00ms 中间走过的节点为: 2037+1935个点 2.在两点之间没有联通关系的情况下 输入参数为:FINDAPATH;V_T_PD_SB_GSGLKG_GEO;482;V_T_PD_SB_DLZDT_GEO;1424;0 修改算法前的效率为: 时间为: 47000.00ms 中间走过的节点为: 284840个点 修改算法后的效率为: 时间为: 73015.00ms 中间走过的节点为: 284614+288803个点

你可能感兴趣的:(c和c++)