dijkstra算法及其优化

不羁岁月 提交于 2020-01-22 20:02:52

dijkstra算法是经典的贪心算法。基本的想法是,有两个集合S和E,开始S集合中只有一个起点,E集合中有剩下的其他点。遍历E集合中的所有点,找出与起点距离最近的一个点,将其加入集合S,并用该点去更新起点到其他点的最短路径。

 

 

由动图结合上面的思路,我们可以看出,算法的基本框架是:

1 1.初始化
2 for i(0 -> n - 1)
3 {
4     2.找出距离起点最近的点
5     3.标记该点加入集合S
6     4.用新加入集合S的点去更新起点到其他点的最短距离
7 }

1.其中初始化包括了距离数组dis,将dis数组初始化为无穷大,将第一个点的距离置为0。

2.循环n - 1次是因为n个点只需添加n - 1个点到集合S,每做一次循环添加一个点,所以是循环n - 1次。

3.标记新点加入集合用一个st数组记录即可。

4.dis[i]表示的是起点s到i的点距离,所以用新点a去更新起点s到其他点(例如b点)的最短路径就是比较dis[a] + (a到b的距离)和 dis[b]的大小,将dis[b]置成两者中的最小值。

5.可以注意的是在伪代码中的2步:找出距离起点最近的点,要遍历剩下的所有点,所以时间复杂度是O(n),但是我们联想到,在一堆数字中找出一个最小值,可以用堆进行优化,时间复杂度是可以由O(n)降到O(logn)的。所以这就是dijkstra的堆优化,可以将时间复杂度为O(mn)降为O(mlogn)。但是在稀疏图中使用较好。稠密图依旧是朴素的dijkstra算法效果更好。

完整代码:

朴素版本:

时间复杂度O(mn)

 1 #include <cstring>
 2 #include <iostream>
 3 #include <algorithm>
 4 
 5 using namespace std;
 6 
 7 const int N = 510;
 8 
 9 int n, m;
10 int g[N][N];
11 int dis[N];
12 bool st[N];
13 
14 int dijkstra()
15 {
16     memset(dis, 0x3f, sizeof dis);//初始化
17     dis[1] = 0;
18 
19     for (int i = 0; i < n - 1; i ++ )
20     {
21         int t = -1;
22         for (int j = 1; j <= n; j ++ )//找出距离起点最近的点
23             if (!st[j] && (t == -1 || dis[t] > dis[j]))
24                 t = j;
25         st[t] = true;//标记加入集合S
26 
27         for (int j = 1; j <= n; j ++ )//用该点去更新其他点
28             dis[j] = min(dis[j], dis[t] + g[t][j]);
29 
30     }
31 
32     if (dis[n] == 0x3f3f3f3f) return -1;
33     return dis[n];
34 }
35 
36 int main()
37 {
38     cin >> n >> m;
39 
40     memset(g, 0x3f, sizeof g);
41     while (m -- )
42     {
43         int a, b, c;
44         cin >> a >> b >> c;
45 
46         g[a][b] = min(g[a][b], c);
47     }
48 
49     cout << dijkstra() << endl;
50 
51     return 0;
52 }
53 
54     

堆优化版本:

时间复杂度O(mlogn)

 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 = 1e5 + 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         PII t = heap.top();
32         heap.pop();
33 
34         int ver = t.second, distance = t.first;
35 
36         if (st[ver]) continue;//如果已经加入集合过就跳过
37         st[ver] = true;
38 
39         for (int i = h[ver]; i != -1; i = ne[i])
40         {
41             int j = e[i];
42             if (dist[j] > distance + w[i])
43             {
44                 dist[j] = distance + 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     cin >> n >> m;
57 
58     memset(h, -1, sizeof h);
59     while (m -- )
60     {
61         int a, b, c;
62         cin >> a >> b >> c;
63         add(a, b, c);
64     }
65 
66     cout << dijkstra() << endl;
67 
68     return 0;
69 }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!