Dijkstra 邻接矩阵 单源点最短路径
来源:互联网 发布:js 私有函数 编辑:程序博客网 时间:2024/06/09 03:54
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">一、定义</span>
<span style="font-family: 新宋体; font-size: 14px; orphans: 2; text-align: -webkit-auto; widows: 2; background-color: rgb(255, 255, 255);"> 最短路径定义:在图中假设边有权值,从一点u到另一点v,如果有多条路径,其中路径权值和最小的路径是最短路径(一条路径的权值等于这条路径上所有边权值加和)</span>
单源点最短路径定义:给定一个源点s,求s到所有点的最短路径。
二、解决的问题
Dijkstra解决的是单源点最短路径问题,解决的是满足特定条件的图的最短路径问题,条件如下:
1. 边权值 >= 0
Dijkstra算法是选出的最短路径权值是递增的,如果出现负权值的边,Dijkstra不能保证结果正确。
三、用到的技术
松弛:
最短路径算法都用到“松弛”技术,即是否能对当前s到v的距离进行改善?表示为:if d[v] > d[u]+w[u,v],不同算法对边松弛的次数和顺序不同。
四、算法详解
给出算法描述(参考<<算法导论>>第三版P383)
Dijkstra(G,w,s)
1 INITIALIZE-SINGLE-SOUREC(G,s)
2 S=Ø
3 Q=G.V
4 while Q!=Ø
5 u=EXTRACT-MIN(Q)
6 S=S∪{u}
7 for each vertex v∈G.adj[u]
8 RELAX(u,v,w)
符号解释:
INITIALIZE-SINGLE-SOUREC(G,s): 初始化操作,主要是两点,使源点s到所有顶点距离d[s,v]等于∞,以及对指示顶点是否已得到最短路径的数组finished[i]置false,(特别的: 初始化d[s] = 0)
S: 大S表示已经得到最短路径的顶点集合
Q: 表示还未得到最短路径的顶点集合
s: 小s表示源点
EXTRACT-MIN(Q): 从未得到最短路径的顶点集合中选出一个当前d[s,v]最小的顶点v',v'就是这一轮选出的顶点,表示v'已经找到最短路径
RELAX: 进行松弛操作,查看是否存在更短的路径,改善距离d[v]
算法解释:
第1行:初始化
第2行:已得到最短路径顶点集合S置空
第3行:未得到最短路径顶点集合Q初始化为图中所有顶点
第5-6行:从未得到最短路径的顶点集合中选出一个当前d[s,v]最小的顶点v',加入S集合,v'就是这一轮选出的顶点,表示v'已经找到最短路径
第7-8行:对上一步得到的v',对v'与其邻接点中未访问过的边进行松弛,即查看通过v'到达其邻接点是否更近
重复4-8,V次,找出s到所有顶点的最短路径
Dijkstra 算法每轮选出一个顶点的最短路径,最短路径是以递增的过程不断选择出来。
下面用图和表来演示Dijkstra如何求得最短路径。
原图: 邻接矩阵:
给定源点V0,初始化得到表格Table 1
Table 1 初始化
i:表示第几次迭代
Vfinished:表示当前次迭代选出的顶点(就是已经求得最短路径的顶点)
S:表示已经得到最短路径的顶点集合
右下方的单元格中,上方表示当前V0到Vi的临时最短距离,下方表示临时最短路径
i=0 第0次迭代
选出当前距离最小顶点V0,将V0加入S集合,对V0出发的边进行松弛(if d[V] > d[V0] + w[V0,V'],更新,V'是V0邻接点),得到Table 2
Table 2 第0次迭代后结果
第0次迭代后,发现通过V0到达V2、V4、V5的距离更近,更新表格
1) 更新距离 d[i] = d[V0] + w[V0,Vi]
2) 更新被更新顶点的最短路径,先将V0的最短路径复制过来,再加上(V0,Vi)
V0最短路径是V0,加上(V0,V2)构成V2当前最短路径V0->V2,加上(V0,V4)构成V4当前最短路径V0->V4,加(V0,V5)构成V5当前最短路径V0->V5
i=1 第1次迭代
选出当前距离最小顶点V2,将V2加入S集合,对V2出发的边进行松弛(if d[V] > d[V2] + w[V2,V'],更新,V'是V2邻接点),得到Table 3
Table 3 第1次迭代后的结果
第1次迭代后,发现通过V2到达V3的距离更近,更新表格
1) 更新距离 d[i] = d[V2] + w[V2,Vi]
2) 更新被更新顶点的最短路径,先将V2的最短路径复制过来,再加上(V2,Vi)
V2最短路径是V0->V2,加上(V2,V3)构成V3当前最短路径V0->V2->V3
i=2 第2次迭代
选出当前距离最小顶点V4,将V4加入S集合,对V4出发的边进行松弛(if d[V] > d[V4] + w[V4,V'],更新,V'是V4邻接点),得到Table 4
Table 4 第2次迭代后结果
第2次迭代后,发现通过V4到达V3、V5的距离更近,更新表格
1) 更新距离 d[i] = d[V3] + w[V4,Vi]
2) 更新被更新顶点的最短路径,先将V4的最短路径复制过来,再加上(V4,Vi)
V4最短路径是V0->V4,加上(V4,V3)构成V3当前最短路径V0->V4->V3,加上(V4,V5)构成V5当前最短路径V0->V4->V5
i=3 第3次迭代
选出当前距离最小顶点V3,将V3加入S集合,对V3出发的边进行松弛(if d[V] > d[V3] + w[V3,V'],更新,V'是V3邻接点),得到Table 5
Table 5 第3次迭代后的结果
第3次迭代后,发现通过V3到达V5的距离更近,更新表格
1) 更新距离 d[i] = d[V3] + w[V3,Vi]
2) 更新被更新顶点的最短路径,先将V3的最短路径复制过来,再加上(V3,Vi)
V3最短路径是V0->V4->V3,加上(V3,V5)构成V5当前最短路径V0->V4->V3->V5
i=4 第4次迭代
选出当前距离最小顶点V5,将V5加入S集合,对V5出发的边进行松弛(if d[V] > d[V5] + w[V5,V'],更新,V'是V5邻接点),得到Table 6
Table 6 第3次迭代后的结果
第4次迭代后,发现没有可以被更新的顶点
i=5 第5次迭代
只剩下一个顶点V1还没有求得最短路径,检查d[V1],发现V0不能到达V1,所以V0到V1没有最短路径
至此,我们就求得了V0到所有点的最短路径及距离。
松弛过程中,对已经得到最短路径的顶点,不再更新距离和最短路径。
五、时间复杂度分析
使用邻接矩阵的Dijkstra算法,时间复杂度在于两点:
1) 每次迭代中,从未得到最短路径的顶点中选择一个距离值最小的顶点
2) 每次得到一个顶点Vi的最短路径后,对Vi出发的边进行松弛
第1点,使用邻接矩阵表示图中,每次迭代都需要遍历一次d[i]来拿到最小值(就是从一个数组中选出一个最小值),这个操作时间复杂度是O(V),总共迭代V次,所以第1点时间复杂度是O(V*V)
第2点,每次迭代中,由于只松弛本次迭代选出的顶点Vi的边,把所有迭代选出的所有顶点松弛的边加起来,这个操作就是松弛所有的边一次,时间复杂度是O(E)
总体计算起来,时间复杂度是O(V*V),V是图中顶点的个数
六、实现
Dijkstra 实现分两种,邻接矩阵和邻接表,细分为:
1) 邻接矩阵
2) 邻接表+二叉堆
3) 邻接表+斐波那契堆
1是邻接矩阵实现,2,3是邻接表实现,这里只实现邻接矩阵的Dijkstra算法。
实现的代码中有很多地方没有验证,功能比较单一,效率有待优化。这里实现的只是有向图,边权值>=0,代码目的只是供初学者参考,有其他需要的读者自行实现。
C/C++ 代码:
#include <iostream>#include <string>#include <vector>#include <utility>#include <cstdlib>using namespace std;const int INFINITE = 100000000;class DG_AdjMatrix{public://初始化图,手动输入DG_AdjMatrix(){cout << "输入有图顶点个数:";cin >> m_vexNum;cout << "输入有向图边数:";cin >> m_arcNum;//先开辟空间m_nodeName = new string[m_vexNum];m_adjMatrix = new int*[m_vexNum];for (int i = 0; i < m_vexNum; ++i){m_adjMatrix[i] = new int[m_vexNum];}for (int i = 0; i < m_vexNum; ++i){for (int j = 0; j < m_vexNum; ++j){m_adjMatrix[i][j] = INFINITE;}}//初始化顶点和边cout << endl;for (int i = 0; i < m_vexNum; ++i){cout << "输入第[" << i+1 << "]个顶点名称: ";cin >> m_nodeName[i];}string x, y;int weight;int locateX, locateY;cout << endl;for (int i = 0; i < m_arcNum; ++i){cout << "输入第[" << i+1 << "]条边依附的顶点和权值: ";cin >> x >> y >> weight;locateX = LocateNode(x);locateY = LocateNode(y);m_adjMatrix[locateX][locateY] = weight;//m_adjMatrix[locateX][locateY] = weight; //无向图}}int LocateNode(string nodeName){for (int i = 0; i < m_vexNum; ++i){if (m_nodeName[i] == nodeName){return i;}}cout << "不存在顶点 " << nodeName << endl;return -1;}//输出邻接矩阵void PrintAdjMatrix(){cout << "vexnum: " << m_vexNum << endl;cout << "arcnum: " << m_arcNum << endl;//print adjMatrixcout << " ";for (int i = 0; i < m_vexNum; ++i){cout << m_nodeName[i] << " ";}cout << endl;for (int i = 0; i < m_vexNum; ++i){cout << m_nodeName[i];for (int j = 0; j < m_vexNum; ++j){if (m_adjMatrix[i][j] != INFINITE){cout << " " << m_adjMatrix[i][j];}else{cout << " " << "-";}}cout << endl;}cout << endl;}int GetVexNum(){return this->m_vexNum;}int GetArcNum(){return this->m_arcNum;}string GetNodeName(int location){if (location >= 0 && location < m_vexNum) //自己做实验可以优化{return this->m_nodeName[location];}else{cout << "不存在这样的顶点" << endl;return "";}}int** GetAdjMatrix(){return this->m_adjMatrix;}/************************************************************************//* dijkstra求单源点最短路径——邻接矩阵 *//************************************************************************/void Dijkstra_AdjMatrix_ShortestPath(DG_AdjMatrix G){string sourceVex;cout << "输入源点: ";cin >> sourceVex;while (LocateNode(sourceVex) == -1){cout << endl;cout << "输入源点: ";cin >> sourceVex;}int src = LocateNode(sourceVex);int *d = new int[G.GetVexNum()];vector<pair<bool, vector<string>>> finished(G.GetVexNum());for (int i = 0; i < G.GetVexNum(); ++i){d[i] = INFINITE;finished[i].first = false;}int presentNode;int minDistance;d[src] = 0;//剩下V-1次,每次选出一个顶点,确定一条最短路径for (int i = 0; i < G.GetVexNum(); ++i){minDistance = INFINITE;for (int w = 0; w < G.GetVexNum(); ++w){if (!finished[w].first && d[w] < minDistance){minDistance = d[w];presentNode = w;}}finished[presentNode].first = true; //最后一次如果某点不可达,presentNode仍是上一次循环值//一个优化:如果只剩下不可达的顶点,通过判断跳出循环//更新距离for (int w = 0; w < G.GetVexNum(); ++w){if (!finished[w].first){ if (minDistance+G.GetAdjMatrix()[presentNode][w] < d[w])//更新距离{d[w] = minDistance + G.GetAdjMatrix()[presentNode][w];finished[w].second.clear();finished[w].second.insert(finished[w].second.begin(),finished[presentNode].second.begin(),finished[presentNode].second.end());finished[w].second.push_back(G.GetNodeName(w));}else if(d[w] != INFINITE && finished[w].second.size()==0)//为了产生路径{finished[w].second.push_back(G.GetNodeName(w));}}}}//输出所有最短路径和距离for (int i = 0; i < G.GetVexNum(); ++i){cout << GetNodeName(src) << " -> " << GetNodeName(i) << " ";if (d[i] != INFINITE){cout << " " << d[i] << " ";cout << GetNodeName(src);for (vector<string>::iterator ix = finished[i].second.begin(); ix != finished[i].second.end(); ++ix){cout << "->" << *ix;}cout << endl;}else{cout << " - ";cout << "unreachable";cout << endl;}}}private:int **m_adjMatrix; //邻接矩阵string *m_nodeName; //结点名称int m_vexNum; //顶点数int m_arcNum; //边数};int main(){DG_AdjMatrix g;g.PrintAdjMatrix();g.Dijkstra_AdjMatrix_ShortestPath(g);system("pause");return 0;}
运行截图:
0 0
- Dijkstra 邻接矩阵 单源点最短路径
- 单源点最短路径----Dijkstra算法
- 单源点最短路径(dijkstra算法)
- Dijkstra求解单源点最短路径
- 单源点最短路径Dijkstra算法的JAVA实现
- 单源点最短路径Dijkstra算法的JAVA实现
- 单源点最短路径算法(dijkstra)
- 单源点最短路径问题(Dijkstra算法)
- 单源点最短路径Dijkstra的java实现
- 单源点最短路径Dijkstra和Bellmanford
- 【数据结构】算法7.15 Dijkstra算法 单源点最短路径
- 单源点最短路径的Dijkstra算法
- 最短路径(Dijkstra算法)也就是单源点问题!
- 单源点最短路径Dijkstra方法实现
- Dijkstra模板求单源点最短路径
- dijkstra算法求解单源点最短路径
- 单源点最短路径
- 单源点最短路径
- 安装搜狗输入法
- MFC 控件变量数据交换
- 选择排序---堆排序算法(Javascript版)
- Xamarin.Ios 下拉菜单,多选,键盘弹出功能
- Scaler算法小结
- Dijkstra 邻接矩阵 单源点最短路径
- scrapy 抓取豆瓣数据(1)
- Facebook pop
- RMI CORBA AIDL IPC RPC 之间的关系
- 09-Dom操作复选框(全选、全不选、反选)
- All is about C!
- SpringAOP切入点表达式
- 音量键的监听 双击退出程序
- 图片在IE能正常显示但是不能在Firefox中显示