一、Dijkstra算法
迪杰斯特拉算法(Dijkstra)用来计算从一个点到其他所有点的最短路径的算法,是一种单源最短路径算法。也就是说,只能计算起点只有一个的情况。它不能处理存在负边权的情况。
利用 dist
数组记录当前各结点与起点的已找到的最短路径长度;
每次从未扩展的结点中选取 dist
值最小的结点 v
进行扩展,更新与 v
相邻的结点的 dist
值;
不断进行上述操作直至所有结点均被扩展,此时 dist
数据中记录的值即为各结点与起点的最短路径长度。
代码实现一:时间复杂度是O(V2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | /**************************************************************** * Description: C++ implementation * How to find Shortest Paths from Source to all Vertices using Dijkstra’s Algorithm * Author: Alex Li * Date: 2023-07-05 21:00:29 * LastEditTime: 2023-07-20 21:17:38 ****************************************************************/ #include <iostream> using namespace std; const int MAXV = 100; int n, i , j, v; int w[MAXV][MAXV]; // 邻接矩阵,记录边长 int pre[MAXV]; //每个结点的前驱 int dist[MAXV]; //存储0点到其它结点的最短路径长度 int used[MAXV]; // 记录结点是否已确定最短距离(0:未确定;1:已确定) //输出结点最短路径 void Path(int u){ if(u==-1)return; Path(pre[u]); cout<<u<<" "; } int main() { cin >> n; //n为结点数 for (i = 0; i < n; i++) for (j = 0; j < n; j++) cin >> w[i][j]; //录入边长 dist[0] = 0; //0点到自身的距离为0 //初始化所有结点距离 for (i = 1; i < n; i++)dist[i] = -1; //初始化所有结点的前驱 for (int i = 0; i <n; i++)pre[i]=-1; //初始化所有结点为未扩展状态 for (i = 0; i < n; i++)used[i] = 0; while (true) { v=-1 ; //v刚开始赋的是-1,意思是还没找到距离最近的点,在循环里满足判断条件的i结点,也就是dist最短的结点 //会赋给v(因为v==-1是在||左边的,当||左边为真时右边不参与运算,不会越界) //当所有结点都已经used[i]=1,则v=-1,while循环结束。 for (i = 0; i < n; i++) if (used[i] != 1 && dist[i] != -1 && (v == -1 || dist[i]<dist[v] )) v=i; //如果没有找到哪个结点还没被访问,或者没有距离的,说明所有结果都已经找好最短路径,v==-1,结束循环。 if(v == -1) break; used[v]=1; //寻找所有v结点的相邻结点,如果没有距离,或者从v点连接距离较短,更新路径和路径前驱 for (i = 0; i < n; i++) if (w[v][i] != -1 && (dist[i] == -1 || dist[v]+w[v][i]<dist[i] )){ dist[i] = dist[v] + w[v][i]; pre[i]=v; } //依次更新每个点所到相邻的点路径值 } //打印每个结点的最短路径的值和经过的结点 for (i = 0; i < n; i++){ cout << "node["<<i<<"]distance is "<<dist[i] <<" and the path is "; Path(i); cout<< endl; } return 0; } |
测试数据:
9 -1 4 -1 -1 -1 -1 -1 8 -1 -1 -1 8 -1 -1 -1 -1 11 -1 -1 8 -1 7 -1 4 -1 -1 2 -1 -1 7 -1 9 14 -1 -1 -1 -1 -1 -1 9 -1 10 -1 -1 -1 -1 -1 4 14 10 -1 2 -1 -1 -1 -1 -1 -1 -1 2 -1 1 6 8 11 -1 -1 -1 -1 1 -1 7 -1 -1 2 -1 -1 -1 6 7 -1
代码实现二: 采用优先队列和邻接表, 时间复杂度是O(ELogV))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | /**************************************************************** * Description: dijkstra-priority_queue * Author: Alex Li * Date: 2024-07-10 18:05:31 * LastEditTime: 2024-07-10 19:06:05 ****************************************************************/ #include <iostream> #include <vector> #include <queue> #include <climits> using namespace std; //dijkstra 算法,使用优先队列 vector <int> dijkstra(int V, vector <vector<pair<int, int> > > &adj, int src){ //创建一个优先队列,存储被处理的结点。 pair.first是权重,pair.second是结点,排序是小的在队列前面,pair(distance, vertex) priority_queue<pair<int, int>,vector<pair<int, int>>,greater<pair<int,int>>> pq; //存结点到起始点的距离,初始值为INT_MAX vector<int> dist(V, INT_MAX); //把起始结点放到优先队列里, pq.push({0, src}); //起始结点到自己的距离是0 dist[src] = 0; //循环,知道优先队列为空 while (!pq.empty()) { // 优先队列中最上面的结点是到起始点距离最短的,拿出来给到u,然后弹出 int u = pq.top().second; pq.pop(); // 遍历所有和u相邻的结点, for (auto& neighbor : adj[u]) { int v = neighbor.first; int weight = neighbor.second; // 如果相邻的结点通过u结点到起始点更短,哪就更新他们到起始点的距离。并把它放到优先队列中。 if (dist[u] + weight < dist[v]) { dist[v] = dist[u] + weight; pq.push({dist[v], v}); } } } //返回距离数组 return dist; } int main(){ int V=5; //cin>>V; //V是图中的结点数 vector<vector<pair<int,int> > > adj(V); //利用邻接表存图 pair.first存结点,pair.second存权重 //添加图的数据,addEdge(u,v,w) 加一条从u到v的边,权重是w adj[0].push_back({1, 10}); adj[0].push_back({4, 3}); adj[1].push_back({2, 2}); adj[1].push_back({4, 4}); adj[2].push_back({3, 9}); adj[3].push_back({2, 7}); adj[4].push_back({1, 1}); adj[4].push_back({2, 8}); adj[4].push_back({3, 2}); int src=0; //起始结点 vector <int> distance=dijkstra(V,adj,src); cout<<"Vertex\tDistance from Source"<<endl; for(int i=0;i<V;i++) cout<<i<<'\t'<<distance[i]<<endl; } |
习题:单源最短路径[P3371]
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m行每行包含三个整数 u,v,w,表示一条 u→v的,长度为 w的边。
输出一行 n 个整数,第 i个表示 s 到第 i个点的最短路径,若不能到达则输出 231−1。
输入 #1
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
输出 #1
0 2 4 3
P4779