dijkstra算法及其優化


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   // 注意這里st[1]不能置成true,這樣在下面的循環內,1號點就不能拓展,所以我們總是在拿出的時候將st置成true
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 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM