算法详解——Dijkstra算法

  Dijkstra算法的目的是寻找单起点最短路径,其策略是贪心加非负加权队列

一、单起点最短路径问题

  单起点最短路径问题:给定一个加权连通图中的特定起点,目标是找出从该起点到图中所有其他顶点的最短路径集合。需要明确的是,这里关心的不仅仅局限于寻找一条从起点出发到任一其他顶点的单一最短路径;单起点最短路径问题要求的是一组路径,每条路径都从起点出发通向图中的一个不同顶点,当然,其中某些路径可能具有公共边。

二、Dijkstra算法原理

  Dijkstra算法是一种高效地找出图中从一个给定起点到所有其他顶点最短路径的方法。它按照距离起点的远近顺序,逐步确定到各个顶点的最短路径。具体来说,算法首先找到距离起点最近的顶点,并确定它们之间的最短路径;然后,它接着寻找下一个最近的顶点,依此类推。在第 iii 次迭代之前,算法已经确定了到达最近的 i−1i-1i1个顶点的最短路径,这些顶点及其路径构成了原图的一个子图,形成一棵以起点为根的树。

  重要的是,由于图中所有边的权重都为非负,算法能够保证每次迭代找到的是当前可达顶点中距离起点最近的一个。这些待选顶点,称为“边缘顶点”,位于已构建的子树的外围。理论上,图中的所有其他顶点也可以被视为边缘顶点,但它们与树中顶点的连接权重被假设为无限大。

  为了求出下一个最接近起点的顶点,Dijkstra算法计算每个边缘顶点至其最近的树内顶点的距离(即该边的权重),并将此距离与从起点到该树内顶点的已知最短路径长度相加。在所有这些候选顶点中,算法选择总和最小的顶点作为下一个最近顶点。Dijkstra算法的核心在于,通过仅对这些特定的候选路径进行比较,就可以有效地找到最短路径。

三、Dijkstra算法应用

  为了简化算法的实施过程,我们为每个顶点引入两个辅助标记。第一个标记是一个数值标记 ddd,它记录了从算法开始到当前为止,从起点到该顶点的最短路径长度。随着算法的进行,当新的顶点被加入到树中时,ddd 的值更新为从起点到这个新顶点的最短路径长度。第二个标记则记录了该路径上的倒数第二个顶点,即当前构建的树中该顶点的父节点(对于起点以及那些尚未与树中的顶点直接相连的顶点,这个标记不必指定)。有了这两个标记后,寻找下一个最近顶点 u∗u^{ *}u 变得相对直接:我们仅需在所有边缘顶点中找到具有最小 ddd 值的顶点即可,而这个查找过程的顺序并不重要。这样,这两个标记极大地简化了算法的步骤,使得确定最短路径的过程更加高效和直观。

在确定了加入树中的顶点u*以后,还需要做两个操作:

  • u∗u^{ *}u 从边缘集合移到树顶点集合中。

  • 对于余下的每一个边缘顶点 uuu,如果通过权重为 w(u∗,u)w(u^{ *}, u)w(u,u) 的边和 u∗u^{ *}u 相连,当 du∗+w(u∗,u)<dud_{u^{*}} +w(u^{*},u)<d_{u}du+w(u,u)<du时,把 uuu 的标记分别更新为 u∗u^{ *}udu∗+w(u∗,u)d_{u^{*}} +w(u^{*},u)du+w(u,u)

在这里插入图片描述
  最短的路径(从左列中的目标项点根据非数字标记向起点回溯,来确定最短路径)和它们的长度(由树中数字标记给出)如下:

  • aaabbb : a−ba-bab, 长度为3

  • aaaddd : a−b−da-b-dabd, 长度为5

  • aaaccc ; a−b−ca-b-cabc, 长度为7

  • aaaeee : a−b−d−ea-b-d-eabde,长度为9

Dijkstra(G, s)
# 单起点最短路径的Dijkstra算法
# 输入: 带有非负权重的连通图G=<V, E>以及起点顶点s
# 输出: 对于V中的每个顶点v,从s到v的最短路径长度d[v],
# 以及路径上的倒数第二个顶点p[v]

Initialize(Q)  # 将顶点优先队列初始化为空
for v in V:
    d[v] ← ∞
    p[v]None
    Insert(Q, v, d[v])  # 初始化优先队列中顶点的优先级

d[s]0
Decrease(Q, s, d[s])  # 更新s的优先级为d[s]
p[s]None

for i from 0 to |V| - 1 do:
    u ← DeleteMin(Q)  # 删除优先级最小的元素
    for 每一个与u相邻的顶点u' do:
        if d[u] + w(u, u') < d[u']:
            d[u'] ← d[u] + w(u, u')
            p[u'] ← u
            Decrease(Q, u', d[u'])
讲解 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; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晓shuo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值