【算法】Dijkstra算法(单源最短路径问题)(路径还原) 邻接矩阵和邻接表实现
Dijkstra算法可使用的前提:不存在负圈。
负圈:负圈又称负环,就是说一个全部由负权的边组成的环,这样的话不存在最短路,因为每在环中转一圈路径总长就会边小。
算法描述:
1.找到最短距离已确定的顶点,从它出发更新相邻顶点的最短距离。
2.以后不需要再关心1中的“最短距离已确定的顶点”。
C++代码:
#include <bits\stdc++.h> using namespace std; #define INF 2147483647 #define MAX_V 1000 #define MAX_E 2000 //单源最短路径问题(Dijkstra算法) int cost[MAX_V][MAX_V]; //cost[u][v]表示e = (u,v)的权值 int d[MAX_V]; //顶点s出发的最短距离 bool used[MAX_V]; //标记使用过的点 int V; //顶点数 void dijkstra(int s){ fill(d, d+V, INF); fill(used, used + V, false); d[s] = 0; while(true){ int v = -1; //找到一个距离最近的没有使用过的点 for(int u = 0;u < V; u++){ if(!used[u] && (v == -1 || d[u] < d[v])) v = u; } //如果所有的点都被使用过了,则break if(v == -1) break; //标记当前点被使用过了 used[v] = true; //更新这个找到的距离最小的点所连的点的距离 for(int u = 0;u < V; u++){ d[u] = min(d[u], d[v] + cost[v][u]); } } } int main(){ }
我们会发现,如果边比较少的话,用邻接矩阵特别耗时间和空间。
时间复杂度O(V^2)
所以边比较少的话,有一种邻接矩阵的写法,对其优化一下,
时间复杂度O(E*log(V))
C++代码:
#include <bits\stdc++.h> using namespace std; #define INF 2147483647 #define MAX_V 1000 #define MAX_E 2000 //单源最短路径问题(Dijkstra算法) struct edge{ int to,cost; }; typedef pair<int, int> P; //first是最短距离,second是顶点的编号 int V; //顶点数 vector <edge> G[MAX_V]; // 边 int d[MAX_V]; // d[i]表示i离源点的最短距离 void dijkstra(int s){ //通过指定greater<P> 参数,优先队列是用堆实现的,堆按照first从小到大排序。 priority_queue<P, vector<P>, greater<P> > que; fill(d, d+V, INF); d[s] = 0; //加源点入最小堆 que.push(P(0,s)); while(!que.empty()){ //取出堆顶的点,也就是距离最小的点 P p = que.top(); que.pop(); int v = p.second; //如果这个点在加入队列之后更新过,就不必再更新 if(d[v] < p.first) continue; //遍历当前点相邻的所有点 for(int i = 0;i < G[v].size(); i++){ edge e = G[v][i]; //如果这个点能更新其他点,就将被更新的那个点加入队列。 if(d[e.to] > d[v] + e.cost){ d[e.to] = d[v] + e.cost; que.push(P(d[e.to], e.to)); } } } } int main(){ }
路径还原:
#include <bits\stdc++.h> using namespace std; #define INF 2147483647 #define MAX_V 1000 #define MAX_E 2000 //单源最短路径问题(Dijkstra算法) int cost[MAX_V][MAX_V]; //cost[u][v]表示e = (u,v)的权值 int d[MAX_V]; //顶点s出发的最短距离 bool used[MAX_V]; //标记使用过的点 int V; //顶点数 int prev[MAX_V]; //最短路径上的前驱顶点 void dijkstra(int s){ fill(d, d+V, INF); fill(used, used + V, INF); fill(prev, prev+V, -1); //初始化前驱数组 d[s] = 0; while(true){ int v = -1; for(int u = 0;u < V; u++){ if(!used[u] && (v == -1 || d[u] < d[v])) v = u; } if(v == -1) break; used[v] = true; for(int u = 0;u < V; u++){ d[u] = min(d[u], d[v] + cost[v][u]); prev[u] = v; //记录每个点的前驱 } } } //获取起始点到顶点t的最短路径 vector <int> getpath(int t){ vector<int> path; while(t != -1){ path.push_back(t); t = prev[t]; } //获取的路径是逆序,需要翻转 reverse(path.begin(),path.end()); return path; } int main(){ }