從零開始養(yǎng)成算法·篇十七:圖的應(yīng)用之最短路徑兩種算法

一唧领、最短路徑的概念:從有向圖中某一頂點(起始頂點)到達(dá)另一頂點(終止頂點)的路徑中,其權(quán)值之和最小的路徑

二呐萌、算法一:Dijkstra算法

單源最短路徑問題:給定一個帶權(quán)有向圖 D 與源點 v ,求從v 到 D 中其它頂點的最短路徑。限定各邊上的權(quán)值大于0悦析。
如何求得這些路徑?迪杰斯特拉(Dijkstra)提出了一個按路徑長度遞增的次序產(chǎn)生最短路徑的算法此衅。首先求出長度最短的一條最短路徑强戴,再參照它求出長度次短的一條最短路徑,依次類推挡鞍,直到從頂點 v 到其它各頂點的最短路徑全部求出為止骑歹。

解決步驟描述:

  1. 設(shè)置輔助數(shù)組dist。它的每一個分量dist[i]表示當(dāng)前找到的從源點 v0到終點 vi的最短路徑的長度墨微;
  2. 初始狀態(tài):
    2.1. 若從源點 v0 到頂點 vi有邊:dist[i]為該邊上的權(quán)值道媚;
    2.2. 若從源點 v0 到頂點 vi無邊:dist[i]為∞。
    根據(jù)以上描述翘县,可以得到如下描述的算法:
    假設(shè)用帶權(quán)的鄰接矩陣Edge[i][j]表示邊(vi,vj)上的權(quán)值最域。若(vi,vj)不存在,則置Edge[i][j]為∞锈麸。S為已.找到從v出發(fā)的最短路徑的終點的集合镀脂,它的初始狀態(tài)為空集。
    1.初始化: S ← {v0 };dist[j] ← Edge[0][j], j = 1, 2, …, n-1;
    2.找出最短路徑所對應(yīng)的點 K:dist[k] == min { dist[i] }, i ∈ V- S ;S ← S U { k };
    3.對于每一個 i ∈ V- S 修改:dist[i] ← min{ dist[i],dist[k] + Edge[k][i] }忘伞;
    4.判斷:若 S = V, 則算法結(jié)束薄翅,否則轉(zhuǎn)2。

算法的精髓:S 集內(nèi)的頂點是已經(jīng)找到最短路徑的頂點氓奈,V0 到 w 的最短路徑只能通過 S 集內(nèi)的頂點,迪杰斯特拉算法的時間復(fù)雜度為O(n*n)翘魄。
算法實現(xiàn)

 G: 網(wǎng)圖;
 v0: V0開始的頂點;
 p[v]: 前驅(qū)頂點下標(biāo);
 D[v]: 表示從V0到V的最短路徑長度和;
 */
void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *P, ShortPathTable *D)
{
    int v,w,k,min;
    k = 0;
    int final[MAXVEX];

    for(v=0; v<G.numVertexes; v++)
    {
        final[v] = 0;
        (*D)[v] = G.arc[v0][v];
        (*P)[v] = 0;
    }

    (*D)[v0] = 0;
    final[v0] = 1;
    (*P)[v0] = -1;

    for(v=1; v<G.numVertexes; v++)
    {
        min=INFINITYC;
        for(w=0; w<G.numVertexes; w++)
        {
            if(!final[w] && (*D)[w]<min)
            {
                k=w;
                min = (*D)[w];
            }
        }
      
        final[k] = 1;
        for(w=0; w<G.numVertexes; w++)
        {
            if(!final[w] && (min + G.arc[k][w]<(*D)[w]))
            {
                (*D)[w] = min + G.arc[k][w];
                (*P)[w]=k;
            }
        }
    }
}

三、算法二:Floyd算法

所有頂點之間的最短路徑:已知一個各邊權(quán)值均大于0的帶權(quán)有向圖舀奶,對每一對頂點 vi≠vj暑竟,要求求出vi與vj之間的最短路徑和最短路徑長度。

解決這個問題的一個方法是:每次以一個頂點為源點育勺,重復(fù)執(zhí)行迪杰斯特拉算法n次光羞。這樣绩鸣,便可求得每一對頂點之間的最短路徑∩炊遥總的執(zhí)行時間為O(n3)呀闻。雖然能實現(xiàn),但是明顯實現(xiàn)起來比較復(fù)雜潜慎。

下面介紹一下由弗洛伊德提出的另一個算法捡多。這個算法的時間復(fù)雜度也是O(n3),但形勢上簡單些。

弗洛伊德算法仍從圖的帶權(quán)鄰接矩陣Edge[i][j]出發(fā)铐炫,其基本思想是:假設(shè)求頂點vi到vj的最短路徑垒手。如果從vi到vj有弧(無向圖稱為邊)倒信,則從vi到vj存在一條長度為Edge[i][j]的路徑科贬,該路徑不一定是最短路徑,尚需n次試探鳖悠。首先考慮路徑(vi 榜掌,v0 ,vj)是否存在(即判別弧(vi 乘综,v0 )和弧(v0 憎账,vj)是否存在)。如果存在,則比較(vi 卡辰,vj)和(vi 胞皱,v0 ,vj)的路徑長度取長度較短者為從vi到vj的中間定點序號不大于0的最短路徑九妈。假如在路徑上再增加一個頂點v1反砌,也就是說,如果(vi 萌朱,…于颖,v1)和(v1,…嚷兔,vj)分別是當(dāng)前找到的中間頂點的序號不大于0的最短路徑,那么(vi ,…做入,v1冒晰,…,vj)就有可能是從vi到vj的中間頂點的序號不大于1的最短路徑竟块。將它和已經(jīng)得到的從vi到vj中間頂點序號不大于0的最短路徑相比較壶运,從中選出中間頂點的序號不大于1的最短路徑之后,再增加一個頂點v2浪秘,繼續(xù)進(jìn)行試探蒋情。依次類推埠况,若vi ,…棵癣,vk)和(vk辕翰,…,vj)分別是從vi到vk和從vk到vj中間頂點的序號不大于k-1的最短路徑,則將(vi 狈谊,…喜命,vk,…河劝,vj)和已經(jīng)找到的從vi到vj且中間頂點序號不大于k-1的最短路徑相比較壁榕,其長度較短者是從vi到vj的中間頂點序號不大于k的最短路徑。這樣赎瞎,經(jīng)過n次比較后牌里,最后求得的必是從vi到vj的最短路徑。按此方法务甥,可以同時求得各對頂點間的最短路徑牡辽。

定義一個 n 階方陣序列:A(-1), A(0), …, A(n-1)
其中:A(-1)[i][j] = Edge[i][j]
A(k) [i][j] = min { A(k-1)[i][j],A(k-1)[i][k] + A(k-1)[k][j]}
k = 0, 1, …, n-1
A(0)[i][j]是從頂點vi到vj, 中間頂點是v0的最短路徑的長度;
A(k) [i][j]是從頂點vi 到vj, 中間頂點的序號不大于k的最短路徑的長度缓呛;
A(n-1)[i]j]是從頂點 vi 到 vj的最短路徑長度催享。

算法實現(xiàn)

Floyd算法,求網(wǎng)圖G中各頂點v到其余頂點w的最短路徑P[v][w]及帶權(quán)長度D[v][w]哟绊。
 Patharc 和 ShortPathTable 都是二維數(shù)組;
 */
void ShortestPath_Floyd(MGraph G, Patharc *P, ShortPathTable *D)
{
    int v,w,k;

    for(v=0; v<G.numVertexes; ++v)
    {
        for(w=0; w<G.numVertexes; ++w)
        {
            (*D)[v][w]=G.arc[v][w];
            (*P)[v][w]=w;
        }
    }
    
    for(k=0; k<G.numVertexes; ++k)
    {
        for(v=0; v<G.numVertexes; ++v)
        {
            for(w=0; w<G.numVertexes; ++w)
            {
                if ((*D)[v][w]>(*D)[v][k]+(*D)[k][w])
                {
                    (*D)[v][w]=(*D)[v][k]+(*D)[k][w];
                    (*P)[v][w]=(*P)[v][k];
                }
            }
        }
    }
}

Dijkstra最短路徑算法是基于遞推的思想設(shè)計的未達(dá)頂點的最短路徑一定是由已達(dá)頂點的最短路徑求出因妙;Floyd最短路徑算法只是Dijkstra最短路徑算法的加強,其本質(zhì)還是遞推票髓。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末攀涵,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洽沟,更是在濱河造成了極大的恐慌以故,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件裆操,死亡現(xiàn)場離奇詭異怒详,居然都是意外死亡,警方通過查閱死者的電腦和手機踪区,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進(jìn)店門昆烁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缎岗,你說我怎么就攤上這事静尼。” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵鼠渺,是天一觀的道長鸭巴。 經(jīng)常有香客問我,道長拦盹,這世上最難降的妖魔是什么鹃祖? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮掌敬,結(jié)果婚禮上惯豆,老公的妹妹穿的比我還像新娘。我一直安慰自己奔害,他們只是感情好楷兽,可當(dāng)我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著华临,像睡著了一般芯杀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雅潭,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天揭厚,我揣著相機與錄音,去河邊找鬼扶供。 笑死筛圆,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的椿浓。 我是一名探鬼主播太援,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扳碍!你這毒婦竟也來了提岔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤笋敞,失蹤者是張志新(化名)和其女友劉穎碱蒙,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體夯巷,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡赛惩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了趁餐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片喷兼。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖澎怒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤喷面,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布星瘾,位于F島的核電站,受9級特大地震影響惧辈,放射性物質(zhì)發(fā)生泄漏琳状。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一盒齿、第九天 我趴在偏房一處隱蔽的房頂上張望念逞。 院中可真熱鬧,春花似錦边翁、人聲如沸翎承。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叨咖。三九已至,卻和暖如春啊胶,著一層夾襖步出監(jiān)牢的瞬間甸各,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工焰坪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留趣倾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓某饰,卻偏偏與公主長得像儒恋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子露乏,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,066評論 2 355