图的有权最短路径

  • 和有向无权图不同的是,有向有权图相邻两个顶点间的边上被赋予一个连接权值。
  • 有权最短路径就是寻找一条路径使得该路径上的权值相加与其他路径相比最小。
  • 例如:
  • 上图,顶点v0v0v_0和v5v5v_5之间存在多条路径,如v0→v3→v5v0→v3→v5v_0 \to v_3 \to v_5、v0→v3→v6→v5v0→v3→v6→v5v_0 \to v_3 \to v_6 \to v_5等,而最短的路径为v0→v3→v6→v5v0→v3→v6→v5v_0 \to v_3 \to v_6 \to v_5长度为1+4+1=6。

图的有权最短路径算法

  • 首先最直观方法是穷举法,穷举出所有可能的路径,再选出最小值。当然复杂度太高不可取。
  • 同无权最短路径一样,求有权最短路径同样借助一个与起始顶点(以v2v2v_2为例)有关的状态信息表。
  • 表中同样有三列信息来表述从v2v2v_2出发,每个顶点的状态改变:
  • 包括Known(标识顶点是否被声明已知)、dist(从v2v2v_2出发使用已知顶点作为中间顶点到目标顶点的距离)、Path(路径中每个顶点上次经过的顶点)。
  • 初始化如下:
  • 我们要求从v2v2v_2出发当所有顶点的有权最短路径就是要想办法更新与之相关的状态信息表。

Dijkstra算法

  • Dijkstra算法是贪心算法(greedy algorithm)的典型应用实例,能够有效地解决有权最短路径问题。
  • 和无权最短路径一样,Dijkstra算法也是按步分阶段进行:
  • 只不过每次采取贪心的策略在未知顶点中选取与起始顶点(v2v2v_2)距离最小的点(Known未知;dist最小)来着手操作。
  • 首先将其(v2v2v_2)Known状态改为已知,接着遍历其(v2v2v_2)邻接点,当邻接点(例如v0、v5v0、v5v_0、v_5)的dist大于其(v2v2v_2)dist加上它们的连接权值时,更新邻接点的dist为其(v2v2v_2)dist加上它们的连接权值。
  • 如此,直到将图中所有v2v2v_2可达的点遍历完,与之有关的状态信息表也更新完毕,最后从状态信息表中可以得出相关信息。
  • 算法复杂度分析:每次寻找最小的dist值花费O(|V|)O(|V|)O(|V|),需要寻找O(|V|)O(|V|)O(|V|)次,故整个算法过程将花费O(|V|2)O(|V|2)O(|V|^2)来查找最小dist值。
  • 每次更新dist为常数时间,共计更新O(|E|)O(|E|)O(|E|)次,所以总得算法时间复杂度为O(|E|+O|V|2)=O(|V|2)O(|E|+O|V|2)=O(|V|2)O(|E|+O|V|^2)=O(|V|^2)。
  • 如果借助优先队列(堆)来查找最小值,时间复杂度可以降低为O(|E|log|V|)O(|E|log⁡|V|)O(|E|\log |V|)。

Dijkstra算法实现

template<class T>
void Graph<T>::Dijkstra(int index)      // Dijkstra算法实现,目的是为了改变index顶点的状态信息表
{Vertex<T> W;int MinIndex;while (1){MinIndex = UnknownMinDistVertex(index);     // 寻找当前index顶点的状态信息表的最小dist值的顶点下标if (MinIndex == -1)     // 当MinIndex为-1,表示所有点都被标明已知,结束循环break;  VSet[index].table[MinIndex].Known = true;       // 标记最小dist值的顶点为已知// 遍历当前最小dist值的顶点的邻接表for (vector<Vertex<T>>::iterator iter = VSet[MinIndex].adj_list.begin(); iter != VSet[MinIndex].adj_list.end();iter++){if (!VSet[index].table[iter->value].Known)  // 如果其邻接点未被声明已知,则if (VSet[index].table[MinIndex].dist + iter->weight < VSet[index].table[iter->value].dist)  {   // 如果最小dist顶点dist加上该顶点与其邻接点的连接权值小于其邻接点原有的dist值,则更新其邻接点的dist值VSet[index].table[iter->value].dist = VSet[index].table[MinIndex].dist + iter->weight;VSet[index].table[iter->value].Path = VSet[MinIndex].value;     // 更新最小dist顶点的邻接点的Path值为最小dist顶点(的值,为了方便)}}}
}

附图的有权最短路径Dijkstra算法实现C++

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<iterator>
using namespace std;const int INF = 999999;     // 表示无穷大(此例中)template<class T> class Vertex;             // 提前声明顶点类template<class T>
class InfoTable {       // 创建一个信息表类
public:bool Known;         // 是否被遍历int dist;           // 顶点间的距离T Path;     // 用顶点关键字表示的路径栏
};template<class T>
class Vertex {      // 创建一个顶点类
public:T value;        // 顶点的关键字值vector<Vertex<T>> adj_list;     // 顶点的邻接表InfoTable<T>* table;        // 最短路径时每个顶点的信息栏int weight;     // 顶点之间的权重(相邻顶点的连接权值),存放在邻接顶点中,每个顶点与自身的权值为0Vertex(T value = 0) :value(value), weight(0) {} // 默认构造函数
};template<class T>
class Graph {       // 创建一个图类
public:vector<Vertex<T>> VSet;     // 表示顶点的集合Graph(int sz) :size(sz) {}  // 构造函数Graph(const Graph<T> &G) { size = G.size;VSet = G.VSet; }   // 复制构造函数void InitInfoTable();       // 初始化图中顶点的状态信息表int UnknownMinDistVertex(int index);    // 找index状态信息表中未知的(Known=0)最小dist顶点(的下标)void Dijkstra(int index);       // Dijkstra算法求某个顶点(图中下标为index的顶点)的有权最短路径void PrintPath(int index);  // 打印某个节点的最短路径private:int size;                   // 图中顶点的个数
};template<class T>
void Graph<T>::InitInfoTable()      // 初始化图中顶点的状态信息表
{for (int i = 0;i < size;i++){VSet[i].table = new InfoTable<T>[size]; // 为每个顶点的状态表申请空间for (int j = 0;j < size;j++){VSet[i].table[j].Known = false;     // 每个节点都没被经过VSet[i].table[j].dist = INF;        // 初始时每个顶点距离为无穷,表示不可达VSet[i].table[j].Path = -1;}VSet[i].table[i].dist = 0;              // 初始时每个顶点距离自身为0}
}template<class T>
int Graph<T>::UnknownMinDistVertex(int index)
{int MinIndex = -1;  // 初始化未知最小dist顶点下标为-1for (int i = 0;i < size;i++)        {if (!VSet[index].table[i].Known)        // 首先找到第一个未知的顶点MinIndex = i;                       // 如果未知点(未被声明已知的顶点)存在,则更新MinIndex的值}for (int i = 0;i < size;i++)                // 再一次遍历index顶点的状态信息表{// 当某个顶点的dist小于当前最小dist值且未知if (VSet[index].table[i].dist < VSet[index].table[MinIndex].dist && !VSet[index].table[i].Known)        MinIndex = i;               // 更新最小dist值下标}return MinIndex;
}template<class T>
void Graph<T>::Dijkstra(int index)      // Dijkstra算法实现,目的是为了改变index顶点的状态信息表
{Vertex<T> W;int MinIndex;while (1){MinIndex = UnknownMinDistVertex(index);     // 寻找当前index顶点的状态信息表的最小dist值的顶点下标if (MinIndex == -1)     // 当MinIndex为-1,表示所有点都被标明已知,结束循环break;  VSet[index].table[MinIndex].Known = true;       // 标记最小dist值的顶点为已知// 遍历当前最小dist值的顶点的邻接表for (vector<Vertex<T>>::iterator iter = VSet[MinIndex].adj_list.begin(); iter != VSet[MinIndex].adj_list.end();iter++){if (!VSet[index].table[iter->value].Known)  // 如果其邻接点未被声明已知,则if (VSet[index].table[MinIndex].dist + iter->weight < VSet[index].table[iter->value].dist)  {   // 如果最小dist顶点dist加上该顶点与其邻接点的连接权值小于其邻接点原有的dist值,则更新其邻接点的dist值VSet[index].table[iter->value].dist = VSet[index].table[MinIndex].dist + iter->weight;VSet[index].table[iter->value].Path = VSet[MinIndex].value;     // 更新最小dist顶点的邻接点的Path值为最小dist顶点(的值,为了方便)}}}
}template<class T>
void Graph<T>::PrintPath(int index)
{cout << "The InfoTable of V" << index << " is:\n";cout << "Vertex Known   dist    Path" << endl;for (int i = 0;i < size;i++)    // 打印下标为index的顶点的状态表{cout << "V" << i << "\t" << VSet[index].table[i].Known << "\t" << VSet[index].table[i].dist<< "\t" << "V" << VSet[index].table[i].Path << endl;}cout << "\nShow the weighted shortest paths from V" << index << " to other vertices by Dijkstra Alogrithm: \n";stack<T> S;         // 借助栈输出从index顶点出发到各个顶点的无权最短路径for (int i = 0;i < size;i++){if (i == index)continue;if (VSet[index].table[i].dist == INF){cout << "Unreachable!\n";continue;}cout << "The shortest path from V" << index << " to V" << i << " is "<< VSet[index].table[i].dist<<" long, and the path is: ";int j = i;      // 每次取一个顶点作为开始S.push(VSet[j].value);  // 顶点入栈while (VSet[index].table[j].Path != -1) // 当遍历指定顶点的“Path”形成路径{S.push(VSet[index].table[j].Path);  // 将路径上的顶点的Path值入栈j = VSet[index].table[j].Path;      // 更新顶点下标}while (!S.empty())                      // 栈不为空时,打印路径并出栈{if(S.top() != index)cout << "->";cout << "V" << S.top();S.pop();}cout << endl;}cout << endl;
}int main()
{Graph<int> G(7);            // 创建一个图对象GVertex<int> V[] = { Vertex<int>(0), Vertex<int>(1), Vertex<int>(2), Vertex<int>(3),Vertex<int>(4), Vertex<int>(5), Vertex<int>(6) };V[0].adj_list.push_back(V[1]);  // 顶点V0的邻接表V[0].adj_list.push_back(V[3]);  V[0].adj_list[0].weight = 2;    // V0与V1的连接权值V[0].adj_list[1].weight = 1;    // V0与V3的连接权值V[1].adj_list.push_back(V[3]);  // 顶点V1的邻接表V[1].adj_list.push_back(V[4]);V[1].adj_list[0].weight = 3;    // V1与V2的连接权值V[1].adj_list[1].weight = 10;   // V1与V4的连接权值V[2].adj_list.push_back(V[0]);  // 顶点V2的邻接表V[2].adj_list.push_back(V[5]);  V[2].adj_list[0].weight = 4;    // V2与V0的连接权值V[2].adj_list[1].weight = 5;    // V2与V5的连接权值V[3].adj_list.push_back(V[2]);  // 顶点V3的邻接表V[3].adj_list.push_back(V[4]);V[3].adj_list.push_back(V[5]);V[3].adj_list.push_back(V[6]);V[3].adj_list[0].weight = 2;    // V3与V2的连接权值V[3].adj_list[1].weight = 2;    // V3与V4的连接权值V[3].adj_list[2].weight = 8;    // V3与V5的连接权值V[3].adj_list[3].weight = 4;    // V3与V6的连接权值V[4].adj_list.push_back(V[6]);  // 顶点V4的邻接表V[4].adj_list[0].weight = 6;    // V4与V6的连接权值V[6].adj_list.push_back(V[5]);  // 顶点V6的邻接表V[6].adj_list[0].weight = 1;    // V6与V5的连接权值for (int i = 0;i < 7;i++){G.VSet.push_back(V[i]);}G.InitInfoTable();      // 初始化图中顶点的状态信息表G.Dijkstra(0);          // Dijkstra算法求G中顶点V2到其他顶点的有权最短路径G.PrintPath(0);         // 打印从V2到其他顶点路径system("pause");return 0;
}
  • 运行结果
The InfoTable of V0 is:
Vertex  Known   dist    Path
V0      1       0       V-1
V1      1       2       V0
V2      1       3       V3
V3      1       1       V0
V4      1       3       V3
V5      1       6       V6
V6      1       5       V3Show the weighted shortest paths from V0 to other vertices by Dijkstra Alogrithm:
The shortest path from V0 to V1 is 2 long, and the path is: V0->V1
The shortest path from V0 to V2 is 3 long, and the path is: V0->V3->V2
The shortest path from V0 to V3 is 1 long, and the path is: V0->V3
The shortest path from V0 to V4 is 3 long, and the path is: V0->V3->V4
The shortest path from V0 to V5 is 6 long, and the path is: V0->V3->V6->V5
The shortest path from V0 to V6 is 5 long, and the path is: V0->V3->V6请按任意键继续. . .

参考资料

Mark Allen Weiss: 数据结构与算法分析

图的有权最短路径及Dijkstra算法相关推荐

  1. 图的单源最短路径:Dijkstra算法实现

    本文介绍的是图的非负权值的单源最短路径问题.问题的提出是,对于有权图D,t提供源点v,要找到从v到其他所有点的最短路径,即单源最短路径问题,在本文中,解决这一问题,是普遍比较熟悉的Dijkstra算法 ...

  2. 图的单源最短路径(Dijkstra算法)

    单源最短路径问题 如果从图中某一顶点(源点)到达另一顶点(终点)的路径可能不止一条,如何找到一条路径使得沿此路径各边上的权值总和达到最小. Dijkstra算法由来 迪杰斯特拉算法(Dijkstra) ...

  3. 数据结构最短路径例题_编程小白暑期进阶笔记45-C语言数据结构与算法最短路径和dijkstra算法...

    最短路径 算法特点: 迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树.该算法常用于路由算法或者作为其他图算法的一个子模块. 算法思路: Dijks ...

  4. 数据结构——最短路径之Dijkstra算法(与最小生成树的prime算法很像,建议一起看)

    最短路径之Dijkstra算法 (一)Dijkstra算法 单源最短路径:就是从某一个顶点出发,到图中任意顶点之间的最短路径: [算法概述]:Dijkstra算法适用于解决单源最短路径的问题.即:从源 ...

  5. 数据结构之图的应用:最短路径(Dijkstra、Floyd)

    图的应用:最短路径 思维导图: 最短路径的定义: BFS算法:(无权单源最短路径) Dijkstra算法:(无权单源.带权单源) Dijkstra算法原理: 算法实现中的辅助数组: 例: 如何通过pa ...

  6. 最短路径之Dijkstra算法

    今天看了最短路径之Dijkstra算法,对这算法,写上自己的心得和感悟! 1.Dijkstra算法,(迪杰斯特拉)--单源最短路径 求的是一个源点到其他顶点的最短路径 算法描述 1).算法思想 设G= ...

  7. 最短路径的Dijkstra算法(邻接表)

    原文:http://blog.csdn.net/axiqia/article/details/50984464 描述 以邻接表作为存储结构实现,求解从给定源点到给定结束点的最短路径. 输入 从1开始表 ...

  8. dijkstra算法PHP,单源最短路径(dijkstra算法)php实现

    做一个医学项目,其中在病例评分时会用到单源最短路径的算法.单源最短路径的dijkstra算法的思路如下: 如果存在一条从i到j的最短路径(Vi.....Vk,Vj),Vk是Vj前面的一顶点.那么(Vi ...

  9. 最短路径问题——Dijkstra算法详解(单源最短路径)

    单源最短路径 单源最短路径,是指从图中任一点出发到其他各点之间的最短路径. Dijkstra算法介绍 Dijkstra算法又称迪杰特斯拉算法,dijkstra算法的核心思想是将全部结点所在集合V分成两 ...

  10. 最短路径(Dijkstra算法),一文必看懂最短路径的方法

    最短路径问题(Dijkstra算法) 从图中的某一个顶点出发到达另一个顶点的所经过的边的权重和最小的一条路径,称为最短路径. Dijkstra算法适用于求一个节点到其他节点的最短路径,主要特点是通过广 ...

最新文章

  1. oracle修改连接数
  2. Spring Cloud比较好的博文地址
  3. 十、简单线性回归的python实现(详解)
  4. 第6章系统数据文件和信息总结
  5. C++ 多继承之如何调用私有成员
  6. flume 一对多hdfs_10PB 规模的 HDFS 数据在 eBay 的迁移实战
  7. (88)FPGA面试题-使用Verilog HDL编写二进制转格雷码
  8. 在MVC 6 .NET核心应用程序中添加种子数据
  9. 用python输出200以内的素数_Python练习题 008:打印101-200之间的所有素数
  10. js字符串截取函数slice()、substring()、substr()
  11. Pygame安装方法(Windows10, Python-3.7.2)
  12. 汉高软件服务器安装系统,如何安装了如指掌眼镜管理系统的服务器和客户端,还需要安装什么软件的?...
  13. 训练集、测试集、验证集之间的区别及理解
  14. 软件测试培训两个月骗局?培训机构到底如何选择?
  15. 433m的模块含义及该如何适当的选择对应的无线模块
  16. nx.adjacency_matrix(G).todense()计算邻接矩阵与真实结果不一致
  17. whois查询的不同结果是什么意思?
  18. js获取传统节假日_vue js moment.js 过滤了双休日和法定节假日
  19. [机器翻译]—BLEU值的计算
  20. 复杂网络分析工具及其比较(…

热门文章

  1. 优化网站加快浏览器访问打开速度
  2. 关于高速光耦6n137的使用总结_高速光耦6n137典型应用电路图汇总(多谐振荡/光电隔离器/光耦开关) - 全文...
  3. xy轴坐标图数字表示_图纸上的坐标x,y轴上有好长的数字表示什么意思
  4. 用html计算长方形的面积公式,长方形的面积公式
  5. 如何把PDF文件转换成其他格式或者转换成可编辑的PDF格式
  6. ”周期天王”20大预言,针针见血,看完整个人通透多了
  7. 使用GCC和Makefile编译c文件
  8. 从 RTL 到 GDS :基于 OpenLANE
  9. easyx带笔锋的涂鸦板!
  10. Java8 Phaser 源码解析