最短路徑 之 Bellman 算法

? 最短路徑 之 Floyd 算法
? 最短路徑 之 Dijkstra 算法

Bellman算法差不多是Floyd算法和Dijkstra算法的結(jié)合體颤诀。

核心代碼

// Bellman-Ford
dis[1] = 0;
for (int k = 1; k < n; k++)
    for (int i = 1; i <= m; i++)
        dis[v[i]] = min(dis[v[i]], dis[u[i]] + w[i]);

Bellman算法需要用鄰接表來儲(chǔ)存圖拼弃。u[i], v[i], w[i]分別表示第i條邊的起點(diǎn)、終點(diǎn)和權(quán)值。dis數(shù)組表示指定頂點(diǎn)(這里是1)到其余各個(gè)頂點(diǎn)的最短路徑尤筐。

上面代碼最后一行表示看看能否通過u[i] --> v[i]這條邊矾端,使得1號(hào)頂點(diǎn)到v[i]號(hào)頂點(diǎn)的距離變短,即1號(hào)頂點(diǎn)到u[i]號(hào)頂點(diǎn)的距離(dis[u[i]])加上u[i] --> v[i]這條邊(權(quán)值為w[i])的值是否比1號(hào)頂點(diǎn)到v[i]號(hào)頂點(diǎn)的距離(dis[v[i]])要小携冤。這一點(diǎn)非常像Dijkstra算法的“松弛”操作悼粮。

再回過頭來看Floyd算法,它的三重循環(huán)k, i, j表示i號(hào)頂點(diǎn)經(jīng)過前k個(gè)頂點(diǎn)到j(luò)號(hào)頂點(diǎn)的最短路徑曾棕。Bellman的二重循環(huán)k, i和這個(gè)類似扣猫,表示進(jìn)行k輪得到1號(hào)頂點(diǎn)最多經(jīng)過k條邊到達(dá)其余頂點(diǎn)的最短路徑。

由于最短路徑不包含回路翘地,所以Bellman算法最多松弛n-1輪申尤,如果在這之后仍然能夠松弛則說明這個(gè)圖里面包含負(fù)權(quán)回路。因此Bellman算法可以用來判斷圖中是否存在負(fù)權(quán)回路衙耕,這是它優(yōu)于Dijkstra和Floyd之處昧穿。

判斷是否存在負(fù)權(quán)回路只需要在循環(huán)完n-1輪后再加下列代碼即可。

bool flag = false;
for  (int i = 1; i <= m; i++)
    if ( dis[v[i]] > dis[u[i]] + w[i] )
        flag = true;

這樣Bellman算法的時(shí)間復(fù)雜度是O(NM)臭杰,比Dijkstra算法還要高粤咪。但事實(shí)上在很多情況下Bellman算法經(jīng)常會(huì)在未達(dá)到n-1輪松弛前就已經(jīng)求得了最短路徑,之后的每一次循環(huán)都不會(huì)有松弛操作渴杆。因此可以用一個(gè)布爾變量來標(biāo)記dis是否發(fā)生了變化寥枝,如果沒有發(fā)生變化就可以跳出循環(huán)。

// Bellman-Ford
for (int k = 1; k < n; k++)
{
    bool check = false;
    for (int i = 1; i <= m; i++)
        if (dis[v[i]] > dis[u[i]] + w[i])
        {
            dis[v[i]] = dis[u[i]] + w[i];
            check = true;
        }
    if(!check)  break;
}

分析Bellman算法的流程磁奖,發(fā)現(xiàn)它有很大的時(shí)間都浪費(fèi)在了循環(huán)中判斷是否需要松弛囊拜。但是在每一次實(shí)施松弛后,就已經(jīng)有一些頂點(diǎn)完成了最短路徑的計(jì)算比搭,在此之后它們的值都不會(huì)再發(fā)生變化冠跷。因此我們就可以每次僅對(duì)最短路徑估計(jì)值發(fā)生變化的頂點(diǎn)的所有出邊進(jìn)行松弛。

隊(duì)列優(yōu)化的Bellman算法

它的操作方法是這樣的:
每次選取隊(duì)首頂點(diǎn)u身诺,對(duì)頂點(diǎn)u的每一條出邊進(jìn)行松弛蜜托。如果松弛成功且這條邊的終點(diǎn)不在隊(duì)列中,就把該頂點(diǎn)放入隊(duì)列中霉赡。由于一個(gè)頂點(diǎn)同時(shí)出現(xiàn)在隊(duì)列中沒有意義橄务,所以可以用一個(gè)數(shù)組book來判重。頂點(diǎn)u松弛完畢后就將u出隊(duì)穴亏。如此循環(huán)直至隊(duì)空蜂挪。
這里需要用到鏈表重挑,我們用數(shù)組來模擬一下。

核心代碼

// Queue-optimized Bellman-Ford
/* 
   first[u[i]]表示頂點(diǎn)u[i]的第一條邊的編號(hào)棠涮,next[i]表示“編號(hào)為i的邊”的“下一條邊”的編號(hào)
   q是C++ STL中的隊(duì)列模板
*/
dis[1] = 0;
book[1] = 1;
q.push(1);
while(!q.empty())
{
    int cur = q.front();
    for (int k = first[cur]; k != -1; k = next[k])
        if (dis[v[k]] > dis[u[k]] + w[k])
        {
            dis[v[k]] = dis[u[k]] + w[k];
            if(!book[k])
            {
                book[k] = 1;
                q.push(k);
            }
        }
    book[cur] = 0;
    q.pop();
}

隊(duì)列優(yōu)化的Bellman算法在形式上類似于廣度優(yōu)先搜索谬哀,但不同之處在于廣搜的時(shí)候一個(gè)頂點(diǎn)出隊(duì)后通常不會(huì)再次入隊(duì)。
當(dāng)一個(gè)頂點(diǎn)入隊(duì)超過n次严肪,那么就可以判斷這個(gè)圖里面存在負(fù)環(huán)史煎。
隊(duì)列優(yōu)化的Bellman算法關(guān)鍵在于只有那些在前一遍松弛成功的頂點(diǎn)才能引起與它們相連頂點(diǎn)的最短路徑估計(jì)值的變化。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诬垂,一起剝皮案震驚了整個(gè)濱河市劲室,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌结窘,老刑警劉巖很洋,帶你破解...
    沈念sama閱讀 218,122評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異隧枫,居然都是意外死亡喉磁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門官脓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來协怒,“玉大人,你說我怎么就攤上這事卑笨≡邢荆” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵赤兴,是天一觀的道長(zhǎng)妖滔。 經(jīng)常有香客問我,道長(zhǎng)桶良,這世上最難降的妖魔是什么座舍? 我笑而不...
    開封第一講書人閱讀 58,636評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮陨帆,結(jié)果婚禮上曲秉,老公的妹妹穿的比我還像新娘。我一直安慰自己疲牵,他們只是感情好承二,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著纲爸,像睡著了一般亥鸠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缩焦,一...
    開封第一講書人閱讀 51,541評(píng)論 1 305
  • 那天读虏,我揣著相機(jī)與錄音,去河邊找鬼袁滥。 笑死盖桥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的题翻。 我是一名探鬼主播揩徊,決...
    沈念sama閱讀 40,292評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼嵌赠!你這毒婦竟也來了塑荒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤姜挺,失蹤者是張志新(化名)和其女友劉穎齿税,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炊豪,經(jīng)...
    沈念sama閱讀 45,655評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凌箕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了词渤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片牵舱。...
    茶點(diǎn)故事閱讀 39,965評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缺虐,靈堂內(nèi)的尸體忽然破棺而出芜壁,到底是詐尸還是另有隱情,我是刑警寧澤高氮,帶...
    沈念sama閱讀 35,684評(píng)論 5 347
  • 正文 年R本政府宣布慧妄,位于F島的核電站,受9級(jí)特大地震影響纫溃,放射性物質(zhì)發(fā)生泄漏腰涧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評(píng)論 3 329
  • 文/蒙蒙 一紊浩、第九天 我趴在偏房一處隱蔽的房頂上張望窖铡。 院中可真熱鬧,春花似錦坊谁、人聲如沸费彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽箍铲。三九已至,卻和暖如春鬓椭,著一層夾襖步出監(jiān)牢的瞬間颠猴,已是汗流浹背关划。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留翘瓮,地道東北人贮折。 一個(gè)月前我還...
    沈念sama閱讀 48,126評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像资盅,于是被迫代替她去往敵國(guó)和親调榄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評(píng)論 2 355

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