迪杰斯特拉算法 | 青训营笔记

171 阅读2分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天

dijkstra算法

Dijkstra算法是一种用于计算一个节点到其他所有节点的最短路径的算法,特别适用于有向图和边权重不为负的情形。

算法的基本思想是通过不断扩展距离源点最近的节点来更新整张图中其他节点的最短路径。它首先将源点标记为已经拓展,并将它的距离设置为0,然后不断地执行以下操作:

选择一个尚未拓展的节点,并将其标记为已经拓展。

对于这个节点的所有邻节点,如果通过已经拓展的节点可以更新它们的最短路径,那么就更新它们的最短路径。

重复以上操作直到所有节点都被拓展为止。

Dijkstra算法的时间复杂度是O(n^2)或O(E + nlogn),其中n是节点数,E是边数。如果使用堆优化,可以将时间复杂度优化到O(E + nlogn)。

题目描述

第一行包含整数 nm

接下来 m 行每行包含三个整数 x,y,z,表示存在一条从点 x 到点 y 的有向边,边长为 z

求解从点1到点n 的最短路径。

普通写法

 #include <iostream>
 #include <cstring>
 using namespace std;
 const int N = 1050;
 int g[N][N];  
 int dist[N];     
 bool vis[N];     
 int n, m;
 void dijkstra()
 {
     dist[1] = 0;
     int v = 1;
     for (int i = 0; i < n; i++)
     {
         int dis = 0x3f3f3f3f;
         for (int j = 1; j <= n; j++)
         {
             if (vis[j]) continue;
             if (dis > dist[j])
             {
                 v = j;
                 dis = dist[j];
             }
         }
         vis[v] = 1;   
         for (int j = 1; j <= n; j++)    
         {
             if (vis[j]) continue;  
             if (dist[j] > dis + g[v][j]) 
             {
                 dist[j] = dis + g[v][j];
             }
         }
     }
 }
 ​
 int main()
 {
     cin >> n >> m;
     memset(dist, 0x3f, sizeof dist);
     memset(g, 0x3f, sizeof g); 
     int a, b, c;
     for (int i = 1; i <= m; i++)
     {
         cin >> a >> b >> c;
         g[a][b] = min(g[a][b], c);
     }
     dijkstra();
     if (dist[n] > 0x3f3f3f3f / 2) cout << -1 << endl;
     else cout << dist[n] << endl;
 }

堆优化版

堆优化版要用单链表存图,如果是稠密图推荐用邻接矩阵存图用普通做法。

优化思路:

因为每次循环我们都要找到下一个离源点最近的点,但简单的做法是比较所有的点,如果每次有新的距离,你把这个距离放到堆里,那么你不需要比较它们,你只需要从堆里取出最上面的元素。

 #include <iostream>
 #include <cstdio>
 #include <queue>
 #include <vector>
 #include <cstring>
 ​
 #define x first
 #define y second
 ​
 using namespace std;
 ​
 typedef pair<int, int> PII;
 const int N = 2e5 + 10;
 ​
 int e[N], h[N], w[N], ne[N], idx;
 ​
 int n, m;
 int dist[N], vis[N];
 ​
 priority_queue<PII, vector<PII>, greater<PII>> q;
 ​
 void add(int a, int b, int c)
 {
     e[idx] = b;
     w[idx] = c;
     ne[idx] = h[a];
     h[a] = idx++;
 }
 ​
 void dijkstra()
 {
     memset(dist, 0x3f, sizeof dist);
     dist[1] = 0;
     q.push({0, 1});
     while(q.size())
     {
         auto t = q.top(); 
         q.pop();         
         int x = t.y, distance = t.x;
         if (vis[x]) continue;
         vis[x] = 1;
         for (int i = h[x]; i != -1; i = ne[i])
         {
             int y = e[i];
             if (dist[y] > dist[x] + w[i])
             {
                 dist[y] = dist[x] + w[i];
                 q.push({dist[y], y});
             }
         }
     }
 }
 ​
 int main()
 {
     cin >> n >> m;
     int a, b, c;
     memset(h, -1, sizeof h);
     for (int i = 1; i <= m; i++)
     {
         cin >> a >> b >> c;
         add(a, b, c);
     }
     dijkstra();
     if(dist[n] > 0x3f3f3f3f / 2) cout << -1 << endl;
     else cout << dist[n] << endl;
     return 0;
 }