Dijkstra(迪傑斯特拉)演算法是基於貪心思想的單源最短路演算法
暴力 Dijkstra
具體如下:
struct node { int v, w; };
vector<node> e[N];
int dist[N], vis[N];
e[u] 存的是節點 u 的所有出邊的終點和邊權,dist[u] 存 u 到原點 s 的最小距離, vis[u] 標記是否走過
void dijkstra(int s) {
memset(dist, 0x3f, sizeof dist); // 初始先讓所有點設為 +∞
dist[s] = 0; // 原點離自身距離為0
for(int i = 1; i < n; i ++) { // 列舉次數
int u = 0;
for(int j = 1; j <= n; j ++) { // 列舉點
if(!vis[j] && dist[j] < dist[u]) u = j; // 選擇一個距離最小的標記
}
vis[u] = 1;
for(auto ed : e[u]) { // 對u的所有出邊,更新鄰邊v的最小距離
int v = ed.v, w = ed.w;
if(dist[v] > dist[u] + w) dist[v] = dist[u] + w;
}
}
}
主函式:
int main() {
cin >> n >> m >> s;
for(int i = 0; i < m; i ++) {
cin >> a >> b >> c;
e[a].push_back({b, c}); // 加入a的鄰邊b和權值c
}
dijkstra(s);
return 0;
}
但是以上的方法對於負邊權不適用,會出錯;
並且時間複雜度為 \(O(n^2 + m) ≈ O(n^2)\)
對於 \(n = 10^3, m = 10^6\) AC, 而對於 \(n = 10^6, m = 10^6\) 則 TLE;
Dijkstra 演算法的堆最佳化 ---- 用優先佇列維護被更新的點的集合
Heap - Dijkstra
具體如下:
priority_queue<pair<int, int>> q;
建立一個pair型別的大根堆 q{-距離, 點},或者是 小根堆 q{距離, 點}; 但小根堆需要自己用結構體加過載運算子
小根堆寫法:
點選檢視程式碼
struct node
{
int first; // 距離
int second; // 點編號
bool operator <(const node &x)const // 過載運算子
{
return x.first<first;
}
};
priority_queue<node> q;
void dijkstra(int s) {
memset(dist, 0x3f, sizeof dist);
dist[s] = 0;
q.push({0, s}); // 原點入隊
while(q.size()) {
auto t = q.top();
q.pop();
int u = t.first; // 從隊頭彈出距離最小的點u
if(vis[u]) continue; //如果走過就直接出隊
vis[u] = 1; //沒走過標記下
for(auto ed : e[u]) {
int v = ed.v, w = ed.w;
if(dist[v] > dist[u] + w) {
dist[v] = dist[u] + w;
q.push({-dist[v], v}); // 把更小的點及距離入隊
}
}
}
}
總結:
暴力 Dijkstra | Heap - Dijkstra | |
---|---|---|
時間複雜度 | \(O(n^2)\) | \(O(mlogm)\) |
圖的大小 | 稠密圖 \(m = O(n^2)\) |
稀疏圖 \(m = O(n)\) |
負邊權 | 不能 | 不能 |