图的最短路
最短路问题分类:
1.单源最短路,也就是只有一个起点终点
单源最短路又可以分成边权为正与边权为负两类
2.多源最短路,其中有多个起点与终点。
存图方式:
1.以邻接矩阵存储(也就是二维数组:g[i] [j] = k表示i到j有一条长度为k的边),这种方式主要适用于稠密图(边数接近点数的平方的图)
2.以领接表存储(链表),这种方式适合稀疏图。
一、单源最短路(且边权为正)
dijkstra算法(主要处理稠密图问题)
数组: dist[i]:表示第i个点到原点的最短距离 g
主要思路:
基于贪心思想。遍历n次,每次找到当前状态下dist值最小的节点,由这个节点发散出去求得的距离也会是最小的,所以此时还要遍历整张图以更新其它点的最短路,并用st数组标记该点已经遍历过 时间复杂度: 由于整个遍历了两次整张图,所以复杂度为O(n ^ 2)
代码实现:
#include <bits/stdc++.h> using namespace std; const int N = 501; int n, m; int g[N][N]; int dist[N]; bool st[N]; int dijkstra() { memset(dist, 0x3f, sizeof(dist)); dist[1] = 0; for(int i = 1; i <= n; i++) { int t = -1; for(int j = 1; j <= n; j++) { if(st[j] == false && (t == -1 || dist[t] > dist[j])) { t = j; }//找到当前dist最小的点,由这个点发散出去的点得到的值也一定是最小的 } st[t] = true; for(int j = 1; j <= n; j++) dist[j] = min(dist[j], dist[t] + g[t][j]); //从当前点更新一次所有其他点的最短路径 } if(dist[n] == 0x3f3f3f3f) return -1; else return dist[n]; } int main() { cin.tie(); cout.tie(); memset(g, 0x3f, sizeof(g)); cin >> n >> m; for(int i = 1; i <= m; i++) { int a, b, c; cin >> a >> b >> c; g[a][b] = min(g[a][b], c);//保证没有自环 } int t = dijkstra(); cout << t << endl; return 0; }
堆优化:
在dijkstra算法的基础上用小根堆维护了上述每次找最小dist的过程 小根堆每次插入数值的时间复杂度为O(logn),且邻接表以边的形式存储。
总时间复杂度为O(mlogn)
#include <bits/stdc++.h> using namespace std; const int N = 1e6 + 10; pair <int, int> PLL; int n, m; int h[N], e[N], ne[N], val[N], idx; int dist[N]; bool st[N]; void add(int a, int b, int c) { e[idx] = b; val[idx] = c; ne[idx] = h[a]; h[a] = idx; idx++; } int dijkstra() { memset(dist, 0x3f, sizeof(dist)); dist[1] = 0; priority_queue <PLL, vector<PLL>, greater<PLL> > heap; //pair在不明确声明的情况下以第一个元素比较 heap.push({0, 1}); while(heap.size() != 0) { PLL t = heap.top(); heap.pop(); int dis = t.first; int ver = t.second; if(st[ver] == true) continue; st[ver] = true; for(int i = h[ver]; i != -1; i = ne[i]) { int j = e[i]; if(dist[j] > dis + val[i]) { dist[j] = dis + val[i]; heap.push({dist[j], j}); } } } if(dist[n] == 0x3f3f3f3f) return -1; else return dist[n]; } int main() { memset(h, -1, sizeof(h)); scanf("%d%d", &n, &m); for(int i = 1; i <= m; i++) { int a, b, c; scanf("%d%d%d", &a, &b, &c); add(a, b, c); } int t = dijkstra(); printf("%d\n", t); return 0; }