Dijkstra算法的原理与实现

       主流的最短路径算法主要有Dijkstra和Floyd算法。相比于Floyd算法,笔者认为Dijkstra算法在理解上更具难度性。因此查阅并参考了诸多博客,书籍资料,浅谈对Dijkstra算法的理解。

1. Dijkstra算法核心思想

        Dijkstra算法是基于贪心算法思想的。所谓贪心算法即始终保持当前迭代解为当前最优解。意思就是在已知的条件下或是当前拥有的全部条件下保证最优解,若在此后的迭代中由于加入了新的条件使得产生了更优解则替代此前的最优解。通过不断的迭代不断保证每次迭代的结果都是当前最优解,那么当迭代到最后一轮时得到的就会是全局最优解

        由于下一轮迭代会参考上一轮的最优解,因此每一轮的迭代的工作量基本一致,降低了整体工作的复杂性。

       在最短路径的问题中,局部最优解即当前的最短路径或者说是在当前的已知条件下起点到其余各点的最短距离。

       到这里读者可能要问了,当前的已知条件指的是啥?难道所有的条件不是问题都已给出的吗?

2.  一个奇妙的构想

       理解Dijkstra算法我们先做一个构想:首先假设问题所给出的所有的顶点和路径都不存在。接下来进行n轮迭代(n为顶点数),每轮迭代加入一个顶点,第一轮加入起点。每加入一个顶点就将与其相连的边加入,但边的另一边的顶点不立即加入。这样就产生了前面我所说的当前的已知条件,即已加入的顶点和边。那么接下来只需要保证起点到其余各顶点距离当前最短即可。

3.  简单的替代原理

       前面我们说了如果下一轮迭代的最短路径比前一轮更短,则需要进行替代,以本轮的路径作为当前最短路径。那么究竟是基于什么进行替代的呢?

图形           

      如上图,从1到3有两条路径,分别是1-->3,1-->2-->3。若我们上一轮迭代的结果为1-->3为最短路径,而本轮结果为1-->2-->3更短,则用1-->2-->3替代1-->3。实际上无论是Dijkstra还是Floyd算法,在进行路径更新时都是由于类似上图这种情况,即到达某一顶点产生了新的路径,而这一路径更短。

      下面我们来以一个例子具体说明Dijkstra迭代过程。

4. Dijkstra算法核心原理

      在2所说的构想的基础上,在初始为空的顶点集合中每轮加入一个未访问过的顶点(第一轮加入起点),并将该顶点相邻的边作为我们新一轮迭代的条件。若新加入的相邻边能到达的顶点距离比上一轮迭代起点到该点的距离更短则更新路径,并将该点加入集合(不更新也加入)。这样就保证了在本轮结束后起点到该新加入的点的距离当前最短。

5. 一个简单的例子

例子

       如上图,以V0为起点,图中已标出各顶点及路径长度,下面我们来迭代计算V0到各顶点的最短距离。

      (1)第一轮:将V0以及V0相邻的V0-->V1,V0-->V3,V0-->V4加入,并更新V0-->V1距离为1,V0-->V3距离为4,V0-->V4距离为4

      (2)第二轮:在(1)三条路径中V0-->V1最短,将V1及V1-->V3加入

      (3)第三轮:由于V1-->V3的加入,计算V0-->V1-->V3 < V0-->V3,因此更新V0到V3路径为V0-->V1-->V3。加入V3及V3-->V4,V3-->V2

      (4)第四轮:V0-->V1-->V3-->V2为唯一路径,更新V0到V2的距离为V0-->V1-->V3-->V2,加入V2及V2-->V5

      (5)第五轮:V0-->V3-->V4 < V0-->V4,不更新,加入V4及V4-->V5

      (6)第六轮:V0-->V4-->V5 < V0-->V1-->V3-->V2-->V5,更新V0到V5的距离为V0-->V1-->V3-->V2-->V5

       迭代完成,到各顶点的最短距离也都完成。下面我们来谈谈代码实现

6. 代码实现

      对于图的存储主要有邻接矩阵和邻接表两种方式,这里我们只谈邻接矩阵,邻接表同理。

      我们需要创建一个邻接矩阵以存储图;一个最短路径数组存储起点到各点的最短路径,算法中不停迭代更新的就是这个数组,初始均为无穷大;一个辅助数组表示各点是否被访问过,因为从上面的例子中可以看出每轮迭代访问的必须是此前未访问过的顶点,初始均为false。至此,所有准备工作完成。

int G[MAXV][MAXV];     //邻接矩阵
int d[MAXV];      //最短距离
bool vis[MAXV] = {false};      //顶点是否访问辅助矩阵

void Dijkstra(int s){
    fill(d, d+MAXV, INF);
    d[s] = 0;
    for(int i=0; i<n; i++){
        int u = -1;    //使d[u]最小
        int MIN = INF;      //MIN存放最小d[u]
        for(int j=0; j<n; j++){
            if(vis[j] == false && d[j] < MIN){
                u = j;
                MIN = d[j];
            }
        }
        if(u == -1) return;      //剩下的顶点和起点s不连通
        vis[u] = true;
        for(int v=0; v<n; v++){
            if(vis[v] == false && G[u][v] != INF && d[u] + G[u][v] < d[v]){
                 d[v] = d[u] + G[u][v]; 
            }
        }
    }
}

      以上代码仅供参考,欢迎指正批评。

  • 11
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
讲解 Dijkstra 算法的基本思想,另外还有算法实现. 当然了,这个算法当路径点上万的时候效率上会降低。 我有另外的改进实现, 上万个点也是在200毫秒内完成。但是不知道怎么添加, 我只能在这里贴关键代码了 : static std::list<Node*> vecNodes; static std::list<Edge*> vecEdges; bool CDijkstras::DijkstrasFindPath(Node* psrcNode, Node* pdstNode, std::list<Node*>& vec, double& fromSrcDist) { if (psrcNode == 0 || pdstNode == 0) return false; if (psrcNode == pdstNode) { vec.push_back(pdstNode); return false; } std::list<Node*>::const_iterator it; for (it=vecNodes.begin(); it!=vecNodes.end(); it++) { (*it)->bAdded = false; (*it)->previous = 0; (*it)->distanceFromStart = MAXDOUBLE; (*it)->smallest = 0; } bool bFindDst = DijkstrasRouteInitialize(psrcNode, pdstNode); fromSrcDist = pdstNode->distanceFromStart; Node* previous = pdstNode; while (previous) { vec.push_back(previous); previous = previous->previous; } m_pDstNode = pdstNode; return bFindDst; } bool CDijkstras::DijkstrasRouteInitialize(Node* psrcNode, Node* pdstNode) { bool bFindDst = false; psrcNode->distanceFromStart = 0; Node* smallest = psrcNode; smallest->bAdded = true; std::list<Node*>::const_iterator it, ait; std::list<Node*> AdjAdjNodes ; for (it=psrcNode->connectNodes.begin(); it!=psrcNode->connectNodes.end(); it++) { if ((*it)->bAdded) continue; (*it)->smallest = psrcNode; (*it)->bAdded = true; AdjAdjNodes.push_back(*it); } while (1) { std::list<Node*> tempAdjAdjNodes; for (it=AdjAdjNodes.begin(); it!=AdjAdjNodes.end(); it++) { Node* curNode = *it; for (ait=curNode->connectNodes.begin(); ait!=curNode->connectNodes.end(); ait++) { Node* pns = *ait; double distance = Distance(pns, curNode) + pns->distanceFromStart; if (distance < curNode->distanceFromStart) { curNode->distanceFromStart = distance; curNode->previous = pns; } if (pns->bAdded == false) { tempAdjAdjNodes.push_back(pns); pns->bAdded = true; } } if (curNode == pdstNode) { bFindDst = true; } } if (bFindDst) break; if (tempAdjAdjNodes.size() == 0) break; AdjAdjNodes.clear(); AdjAdjNodes = tempAdjAdjNodes; } return bFindDst; } // Return distance between two connected nodes float CDijkstras::Distance(Node* node1, Node* node2) { std::list<Edge*>::const_iterator it; for (it=node1->connectEdges.begin(); it!=node1->connectEdges.end(); it++) { if ( (*it)->node1 == node2 || (*it)->node2 == node2 ) return (*it)->distance; } #ifdef _DEBUG __asm {int 3}; #endif return (float)ULONG_MAX; } /****************************************************************************/ /****************************************************************************/ /****************************************************************************/ //得到区域的Key// __int64 CDijkstras::GetRegionKey( float x, float z ) { long xRegion = (long)(x / m_regionWidth); long zRegion = (long)(z / m_regionHeight); __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //得到区域的Key// __int64 CDijkstras::GetRegionKey( long tx, long tz ) { long xRegion = tx ; long zRegion = tz ; __int64 key = xRegion; key <<= 32; key |= ( zRegion & 0x00000000FFFFFFFF ); return key; } //取得一个区域内的所有的路径点, 返回添加的路径点的个数// unsigned long CDijkstras::GetRegionWaypoint (__int64 rkey, std::vector<Node*>& vec) { unsigned long i = 0; SAME_RANGE_NODE rangeNode = mmapWaypoint.equal_range(rkey); for (CRWPIT it=rangeNode.first; it!=rangeNode.second; it++) { i++; Node* pn = it->second; vec.push_back(pn); } return i; } inline bool cmdDistanceNode (Node* pNode1, Node* pNode2) { return pNode1->cmpFromStart < pNode2->cmpFromStart; }; //添加一个路径点// Node* CDijkstras::AddNode (unsigned long id, float x, float y, float z) { Node* pNode = new Node(id, x, y, z); __int64 rkey = GetRegionKey(x, z); mmapWaypoint.insert(make_pair(rkey, pNode)); mapID2Node[id] = pNode; return pNode; } //添加一条边// Edge* CDijkstras::AddEdge (Node* node1, Node* node2, float fCost) { Edge* e = new Edge (node1, node2, fCost); return e; } //通过路径点ID得到路径点的指针// Node* CDijkstras::GetNodeByID (unsigned long nid) { std::map<unsigned long, Node*>::const_iterator it; it = mapID2Node.find(nid); if (it!=mapID2Node.end()) return it->second; return NULL; }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值