單源最短路徑

單源最短路徑

給定一個帶權有向圖G=(V,E),其中每條邊的權是一個實數。另外,還給定V中的一個頂點,稱為源。現在要計算從源到其他所有各頂點的最短路徑長度。這裡的長度就是指路上各邊權之和。這個問題通常稱為單源最短路徑 問題。

Dijkstra算法

問題描述

給定一個帶權有向圖 G=(V,E) ,其中每條邊的權是一個 非負實數。另外,還給定 V 中的一個頂點,稱為源。現在我們要計算從源到所有其他各頂點的最短路徑長度。這裡的長度是指路上各邊權之和。這個問題通常稱為單源最短路徑問題。

Dijkstra算法的解決方案

Dijkstra提出按各頂點與源點v間的路徑長度的遞增次序,生成到各頂點的最短路徑的算法。既先求出長度最短的一條最短路徑,再參照它求出長度次短的一條最短路徑,依次類推,直到從源點v 到其它各頂點的最短路徑全部求出為止。

Dijkstra算法的解題思想

將圖G中所有的頂點V分成兩個頂點集合S和T。以v為源點已經確定了最短路徑的終點併入S集合中,S初始時只含頂點v,T則是尚未確定到源點v最短路徑的頂點集合。然後每次從T集合中選擇S集合點中到T路徑最短的那個點,並加入到集合S中,並把這個點從集合T刪除。直到T集合為空為止。

具體步驟

1、選一頂點v為源點,並視從源點v出發的所有邊為到各頂點的最短路徑(確定數據結構:因為求的是最短路徑,所以①就要用一個記錄從源點v到其它各頂點的路徑長度數組dist[],開始時,dist是源點v到頂點i的直接邊長度,即dist中記錄的是鄰接陣的第v行。②設一個用來記錄從源點到其它頂點的路徑數組path[],path中存放路徑上第i個頂點的前驅頂點)。

2、在上述的最短路徑dist[]中選一條最短的,並將其終點(即<v,k>)k加入到集合s中。

3、調整T中各頂點到源點v的最短路徑。 因為當頂點k加入到集合s中後,源點v到T中剩餘的其它頂點j就又增加了經過頂點k到達j的路徑,這條路徑可能要比源點v到j原來的最短的還要短。調整方法是比較dist[k]+g[k,j]與dist[j],取其中的較小者。

4、再選出一個到源點v路徑長度最小的頂點k,從T中刪去後加入S中,再回去到第三步,如此重複,直到集合S中的包含圖G的所有頂點。

解決方案

pascal:

c++:

c:

Bellman-Ford算法

描述

由於Dijikstra算法對於帶負邊權的圖就無能為力了,但是Bellman-Ford算法就可以解決這個問題。

Bellman-Ford的解題思想

算法基於動態規劃,反覆用已有的邊來更新最短距離,Bellman-Ford算法的核心思想是鬆弛。如果dist[u]和dist[v]滿足dist[v]>dist[u]+map[u][v],dist[v]就應該被更新為dist[u]+map[u][v]。反覆的利用上式對dist數組進行鬆弛,如果沒有負權迴路的話,應當會在n-1次鬆弛後結束。

Bellman-Ford算法的偽代碼

s為源,map[][]記錄圖的信息,map[u][v]為點u到v的邊的長度,結果保存在dist[];

初始化,所有點 i 賦初值dist[i] =+無窮大,出發點為s,dist[s]=0;

對於每條邊(u,v),如果dist[u]!=+無窮大且dist[v]>dist[u]+map[u][v],則dist[v]=dist[u]+map[u][v].

循環步驟2n-1次或直到某次中不在更新,進入步驟4.

對於每條邊(u,v),如果dist[u]!=+無窮大且dist[v]>dist[u]+map[u][v],則存在負權迴路。

1.

初始化,所有點 i 賦初值dist[i] =+無窮大,出發點為s,dist[s]=0;

2.

對於每條邊(u,v),如果dist[u]!=+無窮大且dist[v]>dist[u]+map[u][v],則dist[v]=dist[u]+map[u][v].

3.

循環步驟2n-1次或直到某次中不在更新,進入步驟4.

4.

對於每條邊(u,v),如果dist[u]!=+無窮大且dist[v]>dist[u]+map[u][v],則存在負權迴路。

c:

SPFA算法

描述

SPFA(Shortest Path Faster Algorithm)算法是求單源最短路徑的一種算法,在Bellman-ford算法的基礎上加上一個佇列最佳化,減少了冗餘的鬆弛操作,是一種高效的最短路算法。在 NOI2018Day1T1歸程 中正式被卡,其時間複雜度為O(nm),遠劣於Dijkstra的O((n+m)log m)。

SPFA算法的解題思想

我們約定加權有向圖G不存在負權迴路,即最短路徑一定存在。如果某個點進入佇列的次數超過N次則存在負環(SPFA無法處理帶負環的圖)。當然,我們可以在執行該算法前做一次拓撲排序,以判斷是否存在負權迴路,但這不是我們討論的重點。我們用數組d記錄每個結點的最短路徑估計值,而且用鄰接表來存儲圖G。我們採取的方法是動態逼近法:設立一個先進先出的佇列用來保存待最佳化的結點,最佳化時每次取出隊首結點u,並且用u點當前的最短路徑估計值對離開u點所指向的結點v進行鬆弛操作,如果v點的最短路徑估計值有所調整,且v點不在當前的佇列中,就將v點放入隊尾。這樣不斷從佇列中取出結點來進行鬆弛操作,直至佇列空為止。定理: 只要最短路徑存在,上述SPFA算法必定能求出最小值。

SPFA算法的偽代碼

SPFA實際上是Bellman-Ford基礎上的佇列最佳化

SPFA(G,w,s)

1. INITIALIZE-SINGLE-SOURCE(G,s)

2. INITIALIZE-QUEUE(Q)

3. ENQUEUE(Q,s)

4. While Not EMPTY(Q)

5. Do u<-DLQUEUE(Q)

6. For 每條邊(u,v) in E[G]

7. Do tmp<-d[v]

8. Relax(u,v,w)

9. If (d[v] < tmp) and (v不在Q中)

10. ENQUEUE(Q,v)

c++:

boolSPFA(intsource)

{

deque<int>dq;

inti,j,x,to;

for(i=1;i<=nodesum;i++)

{

in_sum[i]=0;

in_queue[i]=false;

dist[i]=INF;

path[i]=-1;

}

dq.push_back(source);

in_sum[source]++;

dist[source]=0;

in_queue[source]=true;

//初始化完成

while(!dq.empty())

{

x=dq.front();

dq.pop_front();

in_queue[x]=false;

for(i=0;i<adjmap[x].size();i++)

{

to=adjmap[x][i].to;

if((dist[x]<INF)&&(dist[to]>dist[x]+adjmap[x][i].weight))

{

dist[to]=dist[x]+adjmap[x][i].weight;

path[to]=x;

if(!in_queue[to])

{

in_queue[to]=true;

in_sum[to]++;

if(in_sum[to]==nodesum)

return false;

if(!dq.empty())

{

if(dist[to]>dist[dq.front()])

dq.push_back(to);

else

dq.push_front(to);

}

else

dq.push_back(to);

}

}

}

}

returntrue;

}

相關詞條

相關搜尋

熱門詞條

聯絡我們