Dijkstra算法求最短路

发布时间 2023-04-16 14:04:12作者: 春日不曾谋面

一 、Dijkstra 只适用于单源最短路中所有边权都是正数的情况

二 、存储方式

    1、稠密图用邻接矩阵

    2、稀疏图用邻接表

三 、算法实现

  • 用一个 dist 数组保存源点到其余各个节点的距离,dist[i] 表示源点到节点 i 的距离。将dist数组赋值为正无穷,dist[1]=0

  • 用一个状态数组 st 记录是否找到了源点到该节点的最短距离,state[i] 如果为真,则表示找到了源点到节点 i 的最短距离,state[i] 如果为假,则表示源点到节点 i 的最短距离还没有找到。

  • 遍历 dist 数组,找到一个节点 : 没有确定最短路径的节点中距离源点最近的点。假设该节点编号为 i。此时就找到了源点到该节点的最短距离,state[i] 赋值1

      

  • 遍历 i 所有可以到达的节点 j,如果 dist[j] 大于 dist[i] 加上 i->j 的距离 更新 dist[j] = min(dist[j] , dist[i] + w[i][j])

      

 

  • 重复以上步骤一直到个点的state状态为真    

code:

1 : 邻接表实现

 1 #include<iostream>
 2 #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
 3 using namespace std;
 4 const int N = 101010;
 5 
 6 int e[N], ne[N], h[N], w[N], idx;//邻接表
 7 
 8 int dist[N], st[N];        //dist[i] 1号点到i号点的最短距离  st[i] i号点的最短路距离是否确定
 9 
10 int n, m, x, y, z;
11 
12 void add(int a, int b,int c) {
13     e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
14 }
15 void Dijkstra() {
16     memset(dist, 0x3f, sizeof dist);
17     dist[1] = 0;
18     for (int i = 1; i <= n; i++)
19     {
20         int t = -1;
21 
22         for (int j = 1; j <= n; j++)
23             if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
24 
25         for (int k = h[t]; k != -1; k = ne[k])
26         {
27             int i = e[k];
28             if (dist[i] > dist[t] + w[k]) dist[i] = dist[t] + w[k];
29         }
30     }
31 }
32 int main()
33 {
34     fast;
35     memset(h, -1, sizeof h);
36 
37     cin >> n >> m;
38 
39     while (m--) {
40     
41         cin >> x >> y >> z;
42 
43         add(x, y, z);
44 
45     }
46 
47     Dijkstra();
48 
49     cout << dist[n];
50 
51     return 0;
52 }

 

2 : 邻接矩阵实现

 1 #include<iostream>
 2 #include<cstring>
 3 #define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
 4 #define itn int
 5 using namespace std;
 6 
 7 const int N = 510;
 8 
 9 int n, m;
10 int map[N][N];
11 bool st[10010];
12 int dist[N];
13 
14 int dijkstra() {
15     memset(dist, 0x3f, sizeof dist);
16     dist[1] = 0;
17 
18     for (int i = 1; i <= n; i++)
19     {
20         int t = -1;
21 
22         for (int j = 1; j <= n; j++)
23             if (!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
24 
25         st[t] = true;
26 
27         for (int k = 1; k <= n; k++)
28             dist[k] = min(dist[k], dist[t] + map[t][k]);
29 
30     }
31     if (dist[n] == 0x3f3f3f3f) return-1;
32     return dist[n];
33 }
34 
35 int main() {
36     fast;
37 
38     cin >> n >> m;
39 
40     memset(map, 0x3f, sizeof map);
41 
42     for (int j = 1; j <= m; j++)
43     {
44         int a, b, c;
45         cin >> a >> b >> c;
46         map[a][b] = min(map[a][b], c);
47     }
48 
49     int t = dijkstra();
50 
51     cout << t;
52 
53     return 0;
54 }

dikstra算法时间复杂度为O(n^2) 主要耗时的步骤是从dist 数组中选出没有确定最短路径的节点中距离源点最近的点 t。

如果能在一组数中每次能很快的找到最小值,那么时间复杂度可以大大降低,这时候我们就能想到一种数据结构:优先队列

堆里存储距离和节点编号 pair<int,int>

优先队列定义 : priority_queue<Type, Container, Functional> 

Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶

 1 #include <cstring>
 2 #include <iostream>
 3 #include <algorithm>
 4 #include <queue>//堆的头文件
 5 
 6 using namespace std;
 7 
 8 typedef pair<int, int> PII;//堆里存储距离和节点编号
 9 
10 const int N = 1e6 + 10;
11 
12 int n, m;//节点数量和边数
13 int h[N], w[N], e[N], ne[N], idx;//邻接表存储图
14 int dist[N];//存储距离
15 bool st[N];//存储状态
16 
17 void add(int a, int b, int c)
18 {
19     e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
20 }
21 
22 int dijkstra()
23 {
24     memset(dist, 0x3f, sizeof dist);//距离初始化为无穷大
25     dist[1] = 0;
26     priority_queue<PII, vector<PII>, greater<PII>> heap;//小根堆
27     heap.push({0, 1});//插入距离和节点编号
28 
29     while (heap.size())
30     {
31         auto t = heap.top();//取距离源点最近的点
32         heap.pop();
33 
34         int ver = t.second, distance = t.first;//ver:节点编号,distance:源点距离ver 的距离
35 
36         if (st[ver]) continue;//如果距离已经确定,则跳过该点
37         st[ver] = true;
38 
39         for (int i = h[ver]; i != -1; i = ne[i])//更新ver所指向的节点距离
40         {
41             int j = e[i];
42             if (dist[j] > dist[ver] + w[i])
43             {
44                 dist[j] = dist[ver] + w[i];
45                 heap.push({dist[j], j});//距离变小,则入堆
46             }
47         }
48     }
49 
50     if (dist[n] == 0x3f3f3f3f) return -1;
51     return dist[n];
52 }
53 
54 int main()
55 {
56     scanf("%d%d", &n, &m);
57 
58     memset(h, -1, sizeof h);
59     while (m -- )
60     {
61         int a, b, c;
62         scanf("%d%d%d", &a, &b, &c);
63         add(a, b, c);
64     }
65 
66     cout << dijkstra() << endl;
67 
68     return 0;
69 }

总结:

Dijkstra算法适用于求正权有向图中,源点到其余各个节点的最短路径。注意:图中可以有环,但不能有负权边