數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)溫故-5.圖(下):最短路徑(轉(zhuǎn))

數(shù)據(jù)結(jié)構(gòu)基礎(chǔ)溫故-5.圖(下):最短路徑

圖的最重要的應(yīng)用之一就是在交通運(yùn)輸和通信網(wǎng)絡(luò)中尋找最短路徑吞杭。例如在交通網(wǎng)絡(luò)中經(jīng)常會(huì)遇到這樣的問題:兩地之間是否有公路可通外傅;在有多條公路可通的情況下翘骂,哪一條路徑是最短的等等在刺。這就是帶權(quán)圖中求最短路徑的問題,此時(shí)路徑的長(zhǎng)度不再是路徑上邊的數(shù)目總和,而是路徑上的邊所帶權(quán)值的和筑公。帶權(quán)圖分為無(wú)向帶權(quán)圖和有向帶權(quán)圖,但如果從A地到B地有一條公路砚嘴,A地和B地的海拔高度不同十酣,由于上坡和下坡的車速不同,那么邊和邊上表示行駛時(shí)間的權(quán)值也不同际长∷什桑考慮到交通網(wǎng)絡(luò)中的這種有向性,本篇也只討論有向帶權(quán)圖的最短路徑工育。一般習(xí)慣將路徑的開始頂點(diǎn)成為源點(diǎn)虾宇,路徑的最后一個(gè)頂點(diǎn)成為終點(diǎn)。

一如绸、單源點(diǎn)最短路徑

  單源點(diǎn)最短路徑是指給定一個(gè)出發(fā)點(diǎn)(源點(diǎn))和一個(gè)有向圖嘱朽,求出源點(diǎn)到其他各頂點(diǎn)之間的最短路徑。對(duì)于下圖左邊所示的有向圖G怔接,設(shè)頂點(diǎn)0為源點(diǎn)搪泳,則其到其他各頂點(diǎn)的最短路徑如下圖右邊所示。

  從上圖中可以看出扼脐,從源點(diǎn)0到終點(diǎn)4一共有4條路徑:

 “毒(1)0→4      路徑長(zhǎng)度:90

 》芄簟(2)0→3→4     路徑長(zhǎng)度:90

  (3)0→1→2→4  ? 路徑長(zhǎng)度:70

 〖柙蕖(4)0→3→2→4  ? 路徑長(zhǎng)度:60

  可以看出佣谐,源點(diǎn)0到終點(diǎn)4的最短路徑為第(4)條路徑。為了求出最短路徑方妖,前人們已經(jīng)做很多工作狭魂,為我們奉獻(xiàn)了兩個(gè)重要的算法:Dijkstra(迪杰斯特拉)和Floyd(弗洛伊德)算法,下面我們就來(lái)看看這兩個(gè)算法党觅。

二雌澄、Dijkstra算法

2.1 算法思想

  Dijkstra在對(duì)最短路徑的求解方式做了大量的觀察之后,首先提出了按路徑長(zhǎng)度遞增產(chǎn)生與頂點(diǎn)之間的路徑最短的算法仔役,以他的名字稱之為Dijkstra算法掷伙。

Dijkstra算法的基本思想是:將圖中頂點(diǎn)的集合分為兩組S和U,并按最短路徑長(zhǎng)度的遞增次序依次將集合U中的頂點(diǎn)加入到S中又兵,在加入的過程中,總保持從原點(diǎn)v到S中各頂點(diǎn)的最短路徑長(zhǎng)度不大于從原點(diǎn)v到U中任何頂點(diǎn)的最短路徑長(zhǎng)度卒废。

  Dijkstra算法采用鄰接矩陣存儲(chǔ)圖的信息并計(jì)算源點(diǎn)到圖中其余頂點(diǎn)的最短路徑沛厨,如下圖所示。

2.2 算法實(shí)現(xiàn)

 ∷と稀(1)代碼實(shí)現(xiàn)

? ? ?? static void Dijkstra(int[,] cost, int v)

? ? ?? {

? ? ? ? ?? int n = cost.GetLength(1); // 計(jì)算頂點(diǎn)個(gè)數(shù)

? ? ? ? ?? int[] s = new int[n]; ? ?? // 集合S

? ? ? ? ?? int[] dist = new int[n]; ? // 結(jié)果集

? ? ? ? ?? int[] path = new int[n]; ? // 路徑集

?

? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ?? {

? ? ? ? ? ? ?? // 初始化結(jié)果集

? ? ? ? ? ? ?? dist[i] = cost[v, i];

? ? ? ? ? ? ?? // 初始化路徑集

? ? ? ? ? ? ?? if (cost[v, i] > 0)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? // 如果源點(diǎn)與頂點(diǎn)存在邊

? ? ? ? ? ? ? ? ?? path[i] = v;

? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? else

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? // 如果源點(diǎn)與頂點(diǎn)不存在邊

? ? ? ? ? ? ? ? ?? path[i] = -1;

? ? ? ? ? ? ?? }

? ? ? ? ?? }

?

? ? ? ? ?? s[v] = 1; ? // 將源點(diǎn)加入集合S

? ? ? ? ?? path[v] = 0;

?

? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ?? {

? ? ? ? ? ? ?? int u = 0;? // 指示剩余頂點(diǎn)在dist集合中的最小值的索引號(hào)

? ? ? ? ? ? ?? int minDis = int.MaxValue; // 指示剩余頂點(diǎn)在dist集合中的最小值大小

?

? ? ? ? ? ? ?? // 01.計(jì)算dist集合中的最小值

? ? ? ? ? ? ?? for (int j = 0; j < n; j++)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? if (s[j] == 0 && dist[j] > 0 && dist[j] < minDis)

? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ?? u = j;

? ? ? ? ? ? ? ? ? ? ?? minDis = dist[j];

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? }

?

? ? ? ? ? ? ?? s[u] = 1; // 將抽出的頂點(diǎn)放入集合S中

?

? ? ? ? ? ? ?? // 02.計(jì)算源點(diǎn)經(jīng)過頂點(diǎn)u到其余頂點(diǎn)的距離

? ? ? ? ? ? ?? for (int j = 0; j < n; j++)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? // 如果頂點(diǎn)不在集合S中

? ? ? ? ? ? ? ? ?? if (s[j] == 0)

? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ?? // 加入的頂點(diǎn)如與其余頂點(diǎn)存在邊逆皮,并且重新計(jì)算的值小于原值

? ? ? ? ? ? ? ? ? ? ?? if (cost[u, j] > 0 && (dist[j] == 0 || dist[u] + cost[u, j] < dist[j]))

? ? ? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ? ? ?? // 計(jì)算更小的值代替原值

? ? ? ? ? ? ? ? ? ? ? ? ?? dist[j] = dist[u] + cost[u, j];

? ? ? ? ? ? ? ? ? ? ? ? ?? path[j] = u;

? ? ? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? }

? ? ? ? ?? }

?

?

? ? ? ? ?? // 打印源點(diǎn)到各頂點(diǎn)的路徑及距離

? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ?? {

? ? ? ? ? ? ?? if (s[i] == 1)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? Console.Write("從{0}到{1}的最短路徑為:", v, i);

? ? ? ? ? ? ? ? ?? Console.Write(v + "→");

? ? ? ? ? ? ? ? ?? // 使用遞歸獲取指定頂點(diǎn)在路徑上的前一頂點(diǎn)

? ? ? ? ? ? ? ? ?? GetPath(path, i, v);

? ? ? ? ? ? ? ? ?? Console.Write(i + Environment.NewLine + "SUM:");

? ? ? ? ? ? ? ? ?? Console.WriteLine("路徑長(zhǎng)度為:{0}", dist[i]);

? ? ? ? ? ? ?? }

? ? ? ? ?? }

? ? ?? }

?

? ? ?? static void GetPath(int[] path, int i, int v)

? ? ?? {

? ? ? ? ?? int k = path[i];

? ? ? ? ?? if (k == v)

? ? ? ? ?? {

? ? ? ? ? ? ?? return;

? ? ? ? ?? }

?

? ? ? ? ?? GetPath(path, k, v);

? ? ? ? ?? Console.Write(k + "→");

? ? ?? }

  這里經(jīng)歷了三個(gè)步驟,第一步是構(gòu)造集合U参袱,第二步是尋找最短路徑电谣,第三步是重新計(jì)算替換原有路徑。

 ∧ㄊ础(2)基本測(cè)試

  這里要構(gòu)造的有向帶權(quán)圖如下圖所示:

View Code

  運(yùn)行結(jié)果如下圖所示:

  從圖中可以看出剿牺,從源點(diǎn)0到終點(diǎn)4的最短路徑為:0→3→2→4,該路徑長(zhǎng)度為60环壤。

三晒来、Floyd算法

3.1 算法思想

  Floyd(弗洛伊德)算法提出了另外一種用于計(jì)算有向圖中所有頂點(diǎn)間的最短路徑,這種算法成為Floyd算法郑现,它的形式較Dijkstra算法更為簡(jiǎn)單湃崩。

  Floyd算法仍然使用鄰接矩陣存儲(chǔ)的圖,同時(shí)定義了一個(gè)二維數(shù)組A接箫,其中每一個(gè)分量A[i,j]是頂點(diǎn)i到頂點(diǎn)j的最短路徑長(zhǎng)度攒读。另外還使用了另一個(gè)二維數(shù)組path來(lái)保存最短路徑信息。Floyd算法的基本思想如下:

(1)初始時(shí)辛友,對(duì)圖中任意兩個(gè)頂點(diǎn)Vi和Vj捻艳,如果從Vi到Vj存在邊,則從Vi到Vj存在一條長(zhǎng)度為cost[i,j]的路徑蟀给,但該路徑不一定是最短路徑。初始化時(shí)随夸,A[i,j]=cost[i,j]。

(2)在圖中任意兩個(gè)頂點(diǎn)Vi和Vj之間加入頂點(diǎn)Vk震放,如果Vi經(jīng)Vk到達(dá)Vj的路徑存在并更短宾毒,則用A[i,k]+A[k,j]的值代替原來(lái)的A[i,j]值。

(3)重復(fù)步驟(2)殿遂,直到將所有頂點(diǎn)作為中間點(diǎn)依次加入集合中诈铛,并通過迭代公式不斷修正A[i,j]的值,最終獲得任意頂點(diǎn)的最短路徑長(zhǎng)度墨礁。

3.2 算法實(shí)現(xiàn)

 〈敝瘛(1)代碼實(shí)現(xiàn)

? ? ?? static void Floyd(int[,] cost, int v)

? ? ?? {

? ? ? ? ?? int n = cost.GetLength(1);? // 獲取頂點(diǎn)個(gè)數(shù)

? ? ? ? ?? int[,] A = new int[n, n]; ? // 存放最短路徑長(zhǎng)度

? ? ? ? ?? int[,] path = new int[n, n];// 存放最短路徑信息

?

? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ?? {

? ? ? ? ? ? ?? for (int j = 0; j < n; j++)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? // 輔助數(shù)組A和path的初始化

? ? ? ? ? ? ? ? ?? A[i, j] = cost[i, j];

? ? ? ? ? ? ? ? ?? path[i, j] = -1;

? ? ? ? ? ? ?? }

? ? ? ? ?? }

?

? ? ? ? ?? // Flyod算法核心代碼部分

? ? ? ? ?? for (int k = 0; k < n; k++)

? ? ? ? ?? {

? ? ? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? for (int j = 0; j < n; j++)

? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ?? // 如果存在中間頂點(diǎn)K的路徑

? ? ? ? ? ? ? ? ? ? ?? if (i != j && A[i, k] != 0 && A[k, j] != 0)

? ? ? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ? ? ?? // 如果加入中間頂點(diǎn)k后的路徑更短

? ? ? ? ? ? ? ? ? ? ? ? ?? if (A[i, j] == 0 || A[i, j] > A[i, k] + A[k, j])

? ? ? ? ? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? // 使用新路徑代替原路徑

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? A[i, j] = A[i, k] + A[k, j];

? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? path[i, j] = k;

? ? ? ? ? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? }

? ? ? ? ?? }

?

? ? ? ? ?? // 打印最短路徑及路徑長(zhǎng)度

? ? ? ? ?? for (int i = 0; i < n; i++)

? ? ? ? ?? {

? ? ? ? ? ? ?? for (int j = 0; j < n; j++)

? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ?? if (A[i, j] == 0)

? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ?? if (i != j)

? ? ? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ? ? ?? Console.WriteLine("從{0}到{1}沒有路徑!", i, j);

? ? ? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ? ? ?? else

? ? ? ? ? ? ? ? ?? {

? ? ? ? ? ? ? ? ? ? ?? Console.Write("從{0}到{1}的路徑為:", i, j);

? ? ? ? ? ? ? ? ? ? ?? Console.Write(i + "→");

? ? ? ? ? ? ? ? ? ? ?? // 使用遞歸獲取指定頂點(diǎn)的路徑

? ? ? ? ? ? ? ? ? ? ?? GetPath(path, i, j);

? ? ? ? ? ? ? ? ? ? ?? Console.Write(j + " ? ? ");

? ? ? ? ? ? ? ? ? ? ?? Console.WriteLine("路徑長(zhǎng)度為:{0}", A[i, j]);

? ? ? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? }

? ? ? ? ? ? ?? Console.WriteLine();

? ? ? ? ?? }

? ? ?? }

?

? ? ?? static void GetPath(int[,] path, int i, int j)

? ? ?? {

? ? ? ? ?? int k = path[i, j];

? ? ? ? ?? if (k == -1)

? ? ? ? ?? {

? ? ? ? ? ? ?? return;

? ? ? ? ?? }

?

? ? ? ? ?? GetPath(path, i, k);

? ? ? ? ?? Console.Write(k + "→");

? ? ? ? ?? GetPath(path, k, j);

? ? ?? }

  (2)基本測(cè)試

View Code

  運(yùn)行結(jié)果如下圖所示:

參考資料

(1)程杰恩静,《大話數(shù)據(jù)結(jié)構(gòu)》

(2)陳廣焕毫,《數(shù)據(jù)結(jié)構(gòu)(C#語(yǔ)言描述)》

(3)段恩澤,《數(shù)據(jù)結(jié)構(gòu)(C#語(yǔ)言版)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驶乾,一起剝皮案震驚了整個(gè)濱河市邑飒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌级乐,老刑警劉巖疙咸,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異风科,居然都是意外死亡撒轮,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門贼穆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)题山,“玉大人,你說我怎么就攤上這事扮惦⊥沃耄” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵崖蜜,是天一觀的道長(zhǎng)浊仆。 經(jīng)常有香客問我,道長(zhǎng)豫领,這世上最難降的妖魔是什么抡柿? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮等恐,結(jié)果婚禮上洲劣,老公的妹妹穿的比我還像新娘备蚓。我一直安慰自己,他們只是感情好囱稽,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布郊尝。 她就那樣靜靜地躺著,像睡著了一般战惊。 火紅的嫁衣襯著肌膚如雪流昏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天吞获,我揣著相機(jī)與錄音况凉,去河邊找鬼。 笑死各拷,一個(gè)胖子當(dāng)著我的面吹牛刁绒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播烤黍,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼知市,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蚊荣?” 一聲冷哼從身側(cè)響起初狰,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎互例,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筝闹,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媳叨,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了关顷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糊秆。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖议双,靈堂內(nèi)的尸體忽然破棺而出痘番,到底是詐尸還是另有隱情,我是刑警寧澤平痰,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布汞舱,位于F島的核電站,受9級(jí)特大地震影響宗雇,放射性物質(zhì)發(fā)生泄漏昂芜。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一赔蒲、第九天 我趴在偏房一處隱蔽的房頂上張望泌神。 院中可真熱鬧良漱,春花似錦、人聲如沸欢际。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)损趋。三九已至患久,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舶沿,已是汗流浹背墙杯。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留括荡,地道東北人高镐。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像畸冲,于是被迫代替她去往敵國(guó)和親嫉髓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 各校歷年復(fù)試機(jī)試試題 清華邑闲、北大算行、華科試題詳細(xì)筆記部分,少筆記部分與少數(shù)leetcode【含個(gè)人整理筆記】 一苫耸、詳...
    十里江城閱讀 1,177評(píng)論 0 1
  • 參見貪心算法——最短路徑Dijkstra算法參見動(dòng)態(tài)規(guī)劃 目錄 0.最短路徑問題0.1 最短路徑問題描述?0.1....
    王偵閱讀 4,773評(píng)論 1 9
  • https://zh.visualgo.net/graphds 淺談圖形結(jié)構(gòu)https://zh.visualgo...
    狼之獨(dú)步閱讀 4,122評(píng)論 0 0
  • 2015-11-10.周二州邢,天氣晴 3dtouch的實(shí)踐首先,這個(gè)功能只有在iOS9才會(huì)有的褪子,所以首先第一件事就是...
    swweaper5閱讀 339評(píng)論 0 0
  • Demonboy閱讀 302評(píng)論 0 0