單源最短路徑-Dijkstra演算法

Huberyxiao發表於2020-12-21

寫在前面:好久沒看圖論了,今天寫作業的時候發現我已經忘記了。俗話說的好,好記性不如爛筆頭,我翻了翻之前的筆記,竟然沒有關於Dijkstra的(手動狗頭)。趕緊連夜寫了一篇部落格,只為勾引我對Dijkstra的些許記憶。

單源最短路徑-Dijkstra演算法

從一個頂點到其餘各頂點的最短路徑演算法,解決的是有權圖(不存在負權邊)中最短路徑問題

1.演算法思想

它是由E.W.Dijkstra提出的一種按照路徑長度遞增的次序分別產生到各頂點最短路徑的貪心演算法。

      該演算法把帶權圖的的所有頂點分成兩個集合S和V-S。集合S中存放的是已經找到最短路徑的頂點。V-S中存放未找到最短路徑的頂點。演算法按照路徑長度遞增的次序。逐一將V-S中的元素移動到S中。直至V-S為空。

      其中,用到一個非常重要的原理:如果當前v0到vi的最短路徑已經找到,那該條路徑也一定是v0到路徑中所有點的最短路徑。換句話說,如果要找V0到Vi的最短路徑,那麼在Vi之前的i-1個頂點肯定已經找到最短路徑了,即i-1個頂點都在集合S中。

      如圖中,0-4的最短路徑為0-1-7-8-2-5-4.那麼從v0到v8的最短路徑一定是0-1-7-8.

在這裡插入圖片描述

我們以8號結點為例,進行證明:

在這裡插入圖片描述
假設v0-v8的最短路徑是0-7-6-8,那麼也就說明圖中紅色路徑的長度小於0-1-7-8的長度。

那麼從起點0-4的最短路徑就是下圖中的紅色所示。
在這裡插入圖片描述
這與前提不符合,即假設不合理,所以從v0到v8的最短路徑一定是0-1-7-8.

2.基本實現過程

Dijkstra演算法按照從給定起點到圖中頂點的距離,順序求出最短路徑。首先求出從起點到最接近點的頂點之間的最短路徑。然後求出第二近的,一次類推。推而廣之,在第i次迭代開始以前,該演算法已經確定了i-1個頂點的最短路徑。假定上一次(第i-1次迭代)加入集合S的頂點為T。那麼在集合V-S中,與A相鄰的頂點集合稱為“邊緣頂點”,他們作為候選物件。而Dijkstra演算法可以從這些候選物件中選出下一個最接近源點V0的頂點,並把它移動到V-S集合中。

  1. 將所有的頂點分為兩部分:已知最短路徑的頂點集合S和未知最短路徑的頂點集合V-S。可考慮用book陣列實現,book[i]=1,可表示在S中,book[i]=0,則表示在S-V中。
  2. 初始化設定源點V0到自己的最短路徑為0.若存在有源點能直接到達的頂點i,就把V0-Vi的最短路徑設定為對應的長度。同時把其他頂點的最短路徑設定為∞。同時book陣列清零。
  3. 在集合S-V中選擇一個距離遠點最近的頂點i,加入到集合S中。並考察所有以i為起點的邊,對每一條邊進行鬆弛操作。例如存在一條從頂點u到頂點v的邊,則可通過u擴充一條從s到v的路徑即s -> u -> v,這條路徑的長度為dis[ u ]+e[ u ][ v ],如果這個值比dis[ v ]的值要小,我們可以用新值替代當前dis[ v ]中的值
  4. 如此重複第三步,如果集合V-S為空,則結束。最終dis陣列中的值就是源點到各點的最短路徑

實現程式碼模板

void dijkstra(int s)	//s為源點即起點
{
    int i=0,j=0;
    //初始化dis陣列,這裡是1號頂點到其餘各頂點的初始路程
	for(i = 1;i <= n;i++)	//將起點到每一個點的距離存入dis陣列
		dis[i] = e[s][i];   //e[][]陣列是鄰接矩陣村的圖
	
	book[s] = 1;			//起點的最短距離已確定,移入S集合
	
	for(i = 1;i < n;i++)	//迴圈除起點外所有點共n-1次
	{
	    //尋找距離1號頂點最近的頂點
		int minn = INF;
		for(j = 1;j <= n;j++)	//遍歷集合V-S,即book為0的點
		{
			if(dis[j] < minn && book[j] == 0)	//找出這些點中離源點最近的點
			{
				minn = dis[j];	//記錄最近距離
				u = j;		//記錄最近的點
			}
		}
		book[u] = 1;	//頂點u已經確定最短距離,移入集合S,標記為1
		
		//鬆弛:遍歷頂點u的邊緣頂點,藉助剛確定的u頂點作為中轉點重新整理最短距離
		for(j = 1;j <= n;j++)	
			if(e[u][j] < INF)	//如果u點到j點有路
			    //源點s到j點的距離大於藉助u點中轉的距離(就是源點到u的距離加u到j的距離)
				if(dis[j] > dis[u] + e[u][j])	
					dis[j] = dis[u] + e[u][j];	//更新源點到j頂點的最短距離
	}
}

Tips:個人回憶小部落格,不足之處,希望大佬糾正。

相關文章