對dijkstra演算法的常數優化 by azui

2021-07-06 01:09:56 字數 1435 閱讀 6179

dijkstra有乙個經典的堆優化,可以將原本n^2的複雜度優化到(n+m)logn,由於c++的priority_queue,我們只需要自己定義乙個結構體,乙個比較函式,乙個建構函式即可。但分析傳統的堆優化dijkstra模板**可發現,由於每次鬆弛成功都要進行入堆操作,堆中元素個數最多可達到(n+m)那麼多,但其實每加入乙個點,若這個點之前還有若干次在堆中的話,之前在堆中儲存的同乙個點相當於是浪費的,但又佔著空間,由於堆的性質而無法及時彈出。所以堆對於這個問題,並不是最完美的資料結構。

對於這個問題,我針對dijkstra的優化原理為這個演算法專門設計了乙個簡單緊湊的資料結構。思想非常簡單,一顆完全二叉樹,每個節點儲存其子樹中的距離值最小的點,每次自底向上更新即可。這樣一來,空間複雜度限制在了o(n),也就是每次操作的複雜度由原來的log(n+m)降為了嚴格的log(n),但其實最主要的優化不在於此,而是原來被重複鬆弛的點的出堆全部被省去了,也就是總共省去了mlogn這麼多複雜度,在環多而複雜的圖當中這個優化會比較明顯。

下面是依據這個優化寫出的程式(簡稱my)和cqbzoj上"spfa演算法"一題原rank1的**(簡稱std,用的是傳統堆優化dijkstra)的對比實驗。兩篇程式都取消了指定終點已到達的優化,即需要跑出點所有的dis值。為避免誤差,均是在完成了輸入和建邊完成之後計時,儲存邊用的是同樣的手寫離散鍊錶。耗時均為5次執行取平均值。

test1(較小圖,500次執行):        my:3115.4ms   std:3828.6ms

test2(較大多環圖,100次執行):my:4862.4ms   std:7894.2ms

由以上兩組資料看出,加上這個優化的dijkstra實際效率會有明顯的提高。

下面是我寫的**,和傳統堆優化dijkstra的模板**長度幾乎一樣。

#include#include#includeconst int maxn = 50010, maxm = 500010;

const int inf = 0x3f3f3f3f;

using namespace std;

int n, m, s, t;

struct node edge[maxm], *ecnt=edge, *adj[maxn];

void addedge(int a, int b, int c)

int dis[maxn], minp[maxn*2];

bool used[maxn];

inline void pushup(int&p)

void relax(int&p, int d)

void finish(int&p)

void dijkstra()

}int main()

s = 1; t = n; //分別是起點和終點

dijkstra();

printf("%d\n", dis[t]);

return 0;

}

對Dijkstra的思考

迪傑斯特拉演算法是乙個非常典型的求單源最短路徑的演算法 matrix 為鄰接矩陣 node 為單源點 dist為單源點到各點的最短路徑長度 path 記錄最短路徑 public class dijkstra 對node本身初始化 visited node true dist node 0 源點不存在...

Dijkstra演算法的實現

dijkstra演算法的實現 讀入 dijkstratxt 中的部分網路資料 test.txt 弧段起點id,弧段終點id,弧段距離 用dijkstra演算法生成的最佳路徑再寫入 routetxt.txt 檔案中 include include include include using names...

Dijkstra演算法的實現

最近自學了dijkstra演算法,跟著自己的理解寫了個 想加深對dijkstra演算法的理解,如有不足,還請多多指教。首先,dijkstra演算法主要是適用於找兩點之間的最小權值之和。思路 開始的時候,假設有兩個集合 分別為a,b集合 a集合為空 用來存放使用過的點 b集合存放著圖中的各點 當然,在...