深入分析 (迪杰斯特拉算法) Dijkstra 算法实现原理

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。
基本思想

     通过Dijkstra计算图G中的最短路径时,需要指定起点s(即从顶点s开始计算)。

     此外,引进两个集合S和U。S的作用是记录已求出最短路径的顶点(以及相应的最短路径长度),而U则是记录还未求出最短路径的顶点(以及该顶点到起点s的距离)。

     初始时,S中只有起点s;U中是除s之外的顶点,并且U中顶点的路径是"起点s到该顶点的路径"。然后,从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 ... 重复该操作,直到遍历完所有顶点。

操作步骤

(1) 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为"起点s到该顶点的距离"[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。

(2) 从U中选出"距离最短的顶点k",并将顶点k加入到S中;同时,从U中移除顶点k。

(3) 更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。

(4) 重复步骤(2)和(3),直到遍历完所有顶点。

单纯的看上面的理论可能比较难以理解,下面通过实例来对该算法进行说明。

如下图有权图,Dijkstra算法可以计算任意节点其他节点的最短路径

 

算法图解

1.选定A节点并初始化,如上述步骤3所示

2.执行上述 4、5两步骤,找出U集合中路径最短的节点D 加入S集合,并根据条件 if ( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' ) 来更新U集合

 

3.这时候 A->B, A->C 都为3,没关系。其实这时候他俩都是最短距离,如果从算法逻辑来讲的话,会先取到B点。而这个时候 if 条件变成了 if ( 'B 到 C,E 的距离' + 'AB 距离' < 'A 到 C,E 的距离' ) ,如图所示这时候A->B距离 其实为 A->D->B

 

  1. 思路就是这样,往后就是大同小异了

  1. 算法结束

c# 代码实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 迪杰斯特拉算法
{
    struct Graph 
    {
        public char[] vexs;            // 顶点集合
        public int vexnum;             // 顶点数
        public int edgnum;             // 边数
        public int[,] matrix;          // 邻接矩阵
    };

    class Program
    { 
        static void Main(string[] args)
        {
            Graph g;
            g.vexs = new char[]{'A','B','C','D','E'};
            g.vexnum = 5;
            g.edgnum = 7;
            g.matrix = new int[5,5]{
                {0,4,int.MaxValue,2,int.MaxValue},
                {4,0,4,1,int.MaxValue},
                {int.MaxValue,4,0,1,3},
                {2,1,1,0,7},
                {int.MaxValue,int.MaxValue,3,7,0}
            };

            dijkstra(g,0);

            Console.ReadKey();

        }
        private static void dijkstra(Graph g,int vs)
        {
            int k = vs;
            int min = int.MaxValue;
            int tmp;
            int[] flag = new int[100];             // flag[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
            int[] dist = new int[100];
            int[] prev = new int[100];

            for (int i = 0; i < g.vexnum; i++)
            {
                flag[i] = 0;                          // 顶点i的最短路径还没获取到。
                prev[i] = 0;                          // 顶点i的前驱顶点为0。
                dist[i] = g.matrix[vs, i];            // 顶点i的最短路径为"顶点vs"到"顶点i"的权。
            }

            // 对 vs 进行初始化
            flag[vs] = 1;
            dist[vs] = 0;

            for (int i = 1; i < g.vexnum; i++)
            {
                min = int.MaxValue;
                for (int j = 0; j < g.vexnum; j++)
                {
                    if (flag[j] == 0 && dist[j] < min)
                    {
                        min = dist[j];
                        k = j;
                    }
                }

                flag[k] = 1;

                for (int n = 0; n < g.vexnum; n++)
                {
                    tmp = g.matrix[k, n] == int.MaxValue ? int.MaxValue : (min + g.matrix[k, n]);
                    if (flag[n] == 0 && tmp < dist[n])
                    {
                        dist[n] = tmp;
                        prev[n] = k;
                    }
                }

            }

            for (int i = 0; i < g.vexnum; i++)
            {
                Console.WriteLine("点:" + g.vexs[vs] + " 到点:" + g.vexs[i] + "的最短距离为:" + dist[i]);
            }

        }
    }


}

运行结果

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
讲解 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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值