单源最短路径---Dijkstra算法
传送门:
Dijkstra
1、dijkstra算法求解过程:
(1)首先设置两个顶点集合T和S
S中存放已找到最短路径的顶点,初始时,集合S中只有一个顶点,即源点v0
T中存放当前还未找到最短路径的顶点
(2)在集合T中选取当前长度最短的一条最短路径(v0......vk),从而将vk加入到顶点集合S中,并修改远点v0到T中各个顶点的最短路径长度;重复这一步骤,直至所有点加入S为止。
2、算法实现
dist[n]:dist[i]表示当前找到的从源点v0出发到终点vi的最短路径的长度,初始化时,dist[i] = edge[v0][i]
S[n]:S[i]为0表示顶点vi还未加入到集合S中,初始化时S[v0]=1,其余为0
path[n]:path[i]表示v0到vi的最短路径上顶点vi的前一个顶点序号。采用“倒向追踪”方法,确定v0到vi的最短路径上的每个顶点
初始化:dist[k] = edge[v0][k]v0是源点S[v0]=1
递推:
u = min{dist[t]}s[vt] = 0;
u表示当前T集合中dist数组元素值最小的顶点的序号,以后u加入集合S。
dist[k] = min(dist[k], dist[u] + edge[u][k])S[vk] = 0;
https://blog.csdn.net/qq_35644234/article/details/60870719
3.代码实现:
输入:
6 9
0 2 5
0 3 30
1 0 2
1 4 8
2 5 7
2 1 15
4 3 4
5 3 10
5 4 18
输出:
从0到1距离是:20 0->2->1
从0到2距离是: 5 0->2
从0到3距离是:22 0->2->5->3
从0到4距离是:28 0->2->1->4
从0到5距离是:12 0->2->5
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 #include<queue> 7 #include<stack> 8 #include<map> 9 #include<sstream> 10 using namespace std; 11 typedef long long ll; 12 const int maxn = 1e3 + 10; 13 const int INF = 1 << 30; 14 int T, n, m; 15 int Map[maxn][maxn];//邻接矩阵存图 16 int v[maxn];//v[i] = 1表示已加入集合S 17 int d[maxn];//dist[i]表示距离源点的最短距离 18 int path[maxn];//记录路径 19 void dijkstra(int u0)//求顶点为u到其他顶点的最短路 20 { 21 memset(v, 0, sizeof(v)); 22 for(int i = 0; i < n; i++)d[i] = (i == u0 ? 0 : INF); 23 for(int i = 0; i < n; i++)path[i] = -1; 24 for(int i = 0; i < n; i++)//每次加入一个节点,循环n次 25 { 26 int x, m = INF; 27 for(int y = 0; y < n; y++)if(!v[y] && d[y] <= m)m = d[x = y];//记录当前最小值 28 v[x] = 1;//标记已经加入集合 29 for(int y = 0; y < n; y++) 30 { 31 if(d[y] > d[x] + Map[x][y])//松弛操作 32 { 33 d[y] = d[x] + Map[x][y]; 34 path[y] = x; 35 } 36 } 37 } 38 for(int i = 0; i < n; i++) 39 { 40 if(i == u0)continue; 41 printf("从%d到%d距离是:%2d ", u0, i, d[i]); 42 stack<int>q; 43 int x = i; 44 while(path[x] != -1) 45 { 46 q.push(x); 47 x = path[x]; 48 } 49 cout<<u0; 50 while(!q.empty()) 51 { 52 cout<<"->"<<q.top(); 53 q.pop(); 54 } 55 cout<<endl; 56 } 57 } 58 int main() 59 { 60 cin >> n >> m; 61 for(int i = 0; i < n; i++) 62 { 63 for(int j = 0; j < n; j++) 64 { 65 if(i == j)Map[i][j] = 0; 66 else Map[i][j] = INF; 67 } 68 } 69 int x, y, z; 70 for(int i = 0; i < m; i++) 71 { 72 cin >> x >> y >> z; 73 Map[x][y] = z; 74 } 75 dijkstra(0); 76 return 0; 77 }
上述代码时间复杂度O(n2),这里可以将求最小值用优先队列优化,时间复杂度降低到了O(nlog(n)),下面用结构体将其封装起来
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #include<stack> #include<map> #include<sstream> using namespace std; typedef long long ll; const int maxn = 1e3 + 10; const int INF = 1 << 30; int T, n, m; struct edge { int from, to, dist; edge(int u, int v, int d):from(u), to(v), dist(d){} edge(){} }; struct Heapnode { int d, u;//d为距离,u为起点 Heapnode(){} Heapnode(int d, int u):d(d), u(u){} bool operator <(const Heapnode & a)const { return d > a.d;//这样优先队列先取出d小的 } }; struct Dijkstra { int n, m; vector<edge>edges;//存边的信息 vector<int>G[maxn];//G[i]表示起点为i的边的序号集 bool v[maxn];//标记点是否加入集合 int d[maxn];//起点s到各个点的最短路 int p[maxn];//倒叙记录路径 Dijkstra(){} void init(int n) { this -> n = n; for(int i = 0; i < n; i++)G[i].clear(); edges.clear(); } void addedge(int from, int to, int dist) { edges.push_back(edge(from, to, dist)); m = edges.size(); G[from].push_back(m - 1);//存以from为起点的下一条边 } void dijkstra(int s)//以s为起点 { priority_queue<Heapnode>q; for(int i = 0; i < n; i++)d[i] = INF; d[s] = 0; memset(v, 0, sizeof(v)); memset(p, -1, sizeof(p)); q.push(Heapnode(0, s)); while(!q.empty()) { Heapnode now = q.top(); q.pop(); int u = now.u;//当前起点 if(v[u])continue;//如果已经加入集合,continue v[u] = 1; for(int i = 0; i < G[u].size(); i++) { edge& e = edges[G[u][i]];//引用节省代码 if(d[e.to] > d[u] + e.dist) { d[e.to] = d[u] + e.dist; p[e.to] = G[u][i];//记录e.to前的边的编号,p存的是边的下标,这样可以通过边找出之前的点以及每条路的路径,如果用邻接矩阵存储的话这里可以直接存节点u q.push(Heapnode(d[e.to], e.to)); } } } } void output(int u) { for(int i = 0; i < n; i++) { if(i == u)continue; printf("从%d到%d距离是:%2d ", u, i, d[i]); stack<int>q;//存的是边的编号 int x = i;//x就是路径上所有的点 while(p[x] != -1) { q.push(x); x = edges[p[x]].from;//x变成这条边的起点 } cout<<u; while(!q.empty()) { cout<<"->"<<q.top(); q.pop(); } cout<<endl; } } }; Dijkstra ans; int main() { cin >> n >> m; ans.init(n); for(int i = 0; i < m; i++) { int u, v, w; cin >> u >> v >> w; ans.addedge(u, v, w); } int u = 0; ans.dijkstra(u); ans.output(u); }