基於STL優先佇列和鄰接表的dijkstra演算法

2022-05-04 04:15:10 字數 1563 閱讀 2556

首先說下stl優先佇列的侷限性,那就是只提供入隊、出隊、取得隊首元素的值的功能,而dijkstra演算法的堆優化需要能夠隨機訪問佇列中某個節點(來更新源點節點的最短距離)。

看似可以用vector配合make_heap/push_heap/pop_heap來實現這個功能,實際上手動實現就會發現問題所在。比如在dist[v] > dist[u] + cost(u,v)時,需要更新dist[v],然後重新確定v在vector的位置,需要使用push_heap,這樣問題就出現了。

v又在vector的哪個位置呢?只有在vector中乙個個查詢,除非在之前維護最小(距離)堆的時候,每次交換元素,記錄元素的位置變化,也就是用int pos[v];(v為頂點數,下面不再重複說明)來記錄,每次push_heap和pop_heap使堆的元素交換的時候(swap(heap[i], heap[j];)還要順便交換位置(swap(pos[i], pos[j]);)

而僅僅是用stl提供的介面是無法實現的,只有從頭造輪子。

於是有個折中的方法,那就是仍然使用優先佇列。只是在更新點v的最短距離時,把點v重新加入佇列中,而佇列中已經存在的v無法訪問就繼續擱著。

也就是說佇列中有2個點v,乙個是用更新後的距離進行堆操作的,乙個是用更新前的距離進行堆操作的。

首先我不是用while (!q.empty())判斷終止條件的,而是照著書上的for (int i = 0; i < v; i++)判斷,這樣問題就在於,可能點v已經出隊了(代表著已經確定源點到點v的最短路徑),此時若點v出隊則需要跳過。

書上之所以只迴圈v-1次是因為書上用的堆優化,不會像我這樣重複新增某元素到堆中,而是更新堆中元素的權值並移動位置。由於每次迴圈都能確定源點到某個點的最短路徑,所以只需要v-1次足矣。

而退而求其次的直接用優先佇列的做法也可以直接迴圈v-1次,只不過每次迴圈開頭要判斷隊首元素是否已經確定了最短距離,若是則彈出,一直到隊首元素是未確定最短距離。不如while (!q.empty())加continue簡潔(見下面核心**)

auto comp = (int v1, int v2) ;

priority_queue, decltype(comp)> q(comp);

dist[v0] = 0;

q.push(v0);

while (!q.empty())

}}

其他**就不貼了,對其中用到的一些全域性變數做個說明。

注意如果dist是定義在dijkstra函式體內的,lambda表示式要捕獲dist的引用,即auto comp = [&dist](後面不變)

vectoradjlist;  // 鄰接表, 預先讀取了資料

// adjlist是stl容器container的別名(container可以是vector或list或deque),t是adjedge(鄰接邊), 定義如下(省略了建構函式)

struct adjedge ;

vectordist(v, int_max); // 最短距離

vectorpre(v, -1); // 最短路徑上的前1個節點號

dequevis(v, false); // 若求出了最短距離則置為true

Dijkstra 優先佇列 鄰接表優化

為了學習這個優化啊,把dijkstra,和優先佇列,還有map,還有pair,還有vector,之前很散的知識又重現看了一遍 然後讀了好久的dalao下面這個 現在算是懂了一些些了。include include include include include include include inc...

Dijkstra 模板 鄰接表 優先佇列

時間複雜度 o n m logm include include include using namespace std define mp make pair const int n 100010,m 200010 const int inf 0x7fffffff int fr n nex n d...

基於鄰接表的廣度優先搜尋遍歷

description 給定乙個無向連通圖,頂點編號從0到n 1,用廣度優先搜尋 bfs 遍歷,輸出從某個頂點出發的遍歷序列。同乙個結點的同層鄰接點,節點編號小的優先遍歷 input 輸入第一行為整數n 0 n 100 表示資料的組數。對於每組資料,第一行是三個整數k,m,t 0 k 100,0 m...