四大算法解决最短路径问题(Dijkstra+Bellman-ford+SPFA+Floyd)
什么是最短路徑問題?
簡(jiǎn)單來講,就是用于計(jì)算一個(gè)節(jié)點(diǎn)到其他所有節(jié)點(diǎn)的最短路徑。
單源最短路算法:已知起點(diǎn),求到達(dá)其他點(diǎn)的最短路徑。
常用算法:Dijkstra算法、Bellman-ford算法、SPFA算法
多源最短路算法:求任意兩點(diǎn)之間的最短路徑。
常用算法:floyd算法
單源最短路徑——Dijkstra
Dijkstra算法是經(jīng)典的最短路徑算法,用于計(jì)算一個(gè)節(jié)點(diǎn)到其他所有節(jié)點(diǎn)的最短路徑。
主要特點(diǎn)是以起始點(diǎn)為中心向外層層擴(kuò)展,直到擴(kuò)展到終點(diǎn)為止。
時(shí)間復(fù)雜度:O(n^2)
處理問題:單源、無負(fù)權(quán)、有向圖、無向圖最短路徑
不能使用的情況:邊中含有負(fù)權(quán)值(無法判斷)
#define INF 0x3f3f3f3fint e[Max][Max];//e[i][j]代表從i->j的距離,不通設(shè)為無窮大 int dis[Max];//dis[i]代表從起點(diǎn)到i的最短距離 bool book[Max];//book[i]代表點(diǎn)i是否在S中 int n;//n個(gè)頂點(diǎn) int s;//起點(diǎn)void Dijkstra() {for(int i=1;i<=n;i++)//初始化dis數(shù)組dis[i]=e[s][i];for(int i=1;i<=n;i++)//初始化book數(shù)組book[i]=0;dis[s]=0;book[s]=1;for(int i=1;i<=n-1;i++)//Dijkstra算法核心語句 {int minDis=INF;int k;//找到與s最近的頂點(diǎn)kfor(int j=1;j<=n;j++){if(book[j]==0 && dis[j]<minDis){minDis=dis[j];k=j;}}book[k]=1;for(int j=1;j<=n;j++)//“松弛”過程 {if(e[k][j]<INF){if(dis[j]>dis[k]+e[k][j])dis[j]=dis[k]+e[k][j];}}} }基本思想:把帶權(quán)圖中所有的點(diǎn)分為兩部分S∪U,S為已經(jīng)求出從起點(diǎn)到該點(diǎn)的最短路徑的點(diǎn)集合,U中為未確定最短路徑的點(diǎn)集合。把U中的點(diǎn)一個(gè)一個(gè)加入到S中,最后求出全部最短路徑。
如何把U中的點(diǎn)加入S中呢?
①初始時(shí),S只包含源點(diǎn)s,即S={s},dis[s]=0。U包含除v外的其他頂點(diǎn),即U={其余頂點(diǎn)},若s與U中頂點(diǎn)u有邊,則dis[u]=e[s][u],否則,dis[u]=∞。
②從U中找到一個(gè)與源點(diǎn)s距離最小(min(dis[]))的頂點(diǎn)k,把k加入S中,dis[k]確定(仔細(xì)想想,s與k最短路徑必定是dis[k]=e[s][k],找不到更短的)。
③以k為新考慮的中間點(diǎn),修改源點(diǎn)s到U中各頂點(diǎn)的距離dis[]:若從源點(diǎn)s到頂點(diǎn)u的距離(dis[k]+e[k][u],經(jīng)過頂點(diǎn)k)比原來距離(dis[u],不經(jīng)過頂點(diǎn)k)短,則修改頂點(diǎn)u的距離值,修改后的距離值的頂點(diǎn)k的距離加上邊上的權(quán)。(這一過程稱為“松弛”)
④重復(fù)步驟②和③直到所有頂點(diǎn)都包含在S中。
?
算法優(yōu)化:這里面每次都要尋找距離最短的那個(gè)點(diǎn)和距離,時(shí)間復(fù)雜度為O(n),可以用“堆”來優(yōu)化,是時(shí)間復(fù)雜度降為O(lgn)。
?
算法過程詳解:http://ahalei.blog.51cto.com/4767671/1387799
?
單源最短路徑——Bellman-ford算法
求單源最短路徑,可以判斷有無負(fù)權(quán)回路(若有,則不存在最短路), 時(shí)效性較好,時(shí)間復(fù)雜度O(VE)。
處理問題:單源、可有負(fù)權(quán)、有向圖、無向圖最短路徑
注:下面代碼為有向圖最短路徑
#define INF 0x3f3f3f3fstruct Edge{int u;//起int v;//終int weight;//長(zhǎng)度 };Edge edge[maxm];//用來存儲(chǔ)所有的邊 int dis[maxn];//dis[i]表示源點(diǎn)到i的最短距離 int n,m;//n個(gè)點(diǎn),m條邊 int s;//源點(diǎn)bool Bellmen_ford() {for(int i=1;i<=n;i++)//初始化dis[i]=INF;dis[s]=0;//源節(jié)點(diǎn)到自己的距離為0for(int i=1;i<n;i++)//松弛過程,計(jì)算最短路徑 {for(int j=1;j<=m;j++){if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)//比較s->v與s->u->v大小dis[edge[j].v]=dis[edge[j].u]+edge[j].weight;}}for(int j=1;j<=m;j++)//判斷是否有負(fù)邊權(quán)的邊 {if(dis[edge[j].v]>dis[edge[j].u]+edge[j].weight)return false;}return true; }基本思想:bellman-ford的思想和dijkstra很像,其關(guān)鍵點(diǎn)都在于不斷地對(duì)邊進(jìn)行松弛。而最大的區(qū)別就在于前者能作用于負(fù)邊權(quán)的情況。其實(shí)現(xiàn)思路是在求出最短路徑后,判斷此刻是否還能對(duì)便進(jìn)行松弛,如果還能進(jìn)行松弛,便說明還有負(fù)邊權(quán)的邊。
?
單源最短路徑——SPFA算法
上一種算法其實(shí)不好用,復(fù)雜度太高,SPFA算法是Bellman-ford算法的隊(duì)列優(yōu)化,比較常用。SPFA算法在負(fù)邊權(quán)圖上可以完全取代Bellman-ford算法,另外在稀疏圖中也表現(xiàn)良好。但是在非負(fù)邊權(quán)圖中,為了避免最壞情況的出現(xiàn),通常使用效率更加穩(wěn)定的Dijkstra算法,以及它的使用堆優(yōu)化的版本。通常的SPFA算法在一類網(wǎng)格圖中的表現(xiàn)不盡如人意。不是很穩(wěn)定,不如Dijkstra。
處理問題:單源、可有負(fù)權(quán)、有向圖、無向圖最短路徑(自身其實(shí)無法處理負(fù)權(quán))
#define INF 0x3f3f3f3fint dis[MAX];//dis[i]表示起點(diǎn)到i的最短距離 bool vis[MAX];//是否訪問過點(diǎn)i int e[MAX][MAX];//矩陣int n,m;//點(diǎn)和邊的數(shù)量 int s;//源點(diǎn)void SPFA() {for(int i=1;i<=n;i++)//初始化 {dis[i]=INF;vis[i]=false;}queue<int> q;q.push(s);dis[s]=0;vis[s]=true;while(!q.empty()){int cur=q.front();q.pop();vis[cur]=false;for(int i=1;i<=n;i++){if(e[cur][i]!=INF&&dis[i]>=dis[cur]+e[cur][i]){dis[i]=dis[cur]+e[cur][i];if(!vis[i]){vis[i]=true;q.push(i);}}}} }算法思想:
設(shè)立一個(gè)隊(duì)列用來保存待優(yōu)化的點(diǎn),優(yōu)化時(shí)每次取出隊(duì)首結(jié)點(diǎn)u,并且用u點(diǎn)當(dāng)前的最短路徑估計(jì)值對(duì)u點(diǎn)所指向的結(jié)點(diǎn)v進(jìn)行松弛操作,如果v點(diǎn)的最短路徑估計(jì)值有所調(diào)整,且v點(diǎn)不在當(dāng)前的隊(duì)列中,就將v點(diǎn)放入隊(duì)尾。這樣不斷從隊(duì)列中取出結(jié)點(diǎn)來進(jìn)行松弛操作,直至隊(duì)列空為止。
?
算法過程詳解:http://www.360doc.com/content/13/1208/22/14357424_335569176.shtml
例題:http://ac.jobdu.com/problem.php?pid=1008
?
多源最短路徑——Floyd算法
Floyd算法是一種利用動(dòng)態(tài)規(guī)劃思想的計(jì)算加權(quán)圖中多源點(diǎn)之間最短路徑的算法。可以正確處理有向圖或負(fù)權(quán)的最短路徑問題。
時(shí)間復(fù)雜度:O(N^3)
空間復(fù)雜度:O(N^2)
處理問題:多源、可有負(fù)權(quán)、有向圖、無向圖最短路徑
int e[Max][Max];//e[i][j]代表從i->j的距離,不通設(shè)為無窮大 int n;//n個(gè)頂點(diǎn) //Floyd算法 void Floyd() {for(int k=1;k<=n;k++)//遍歷所有的中間點(diǎn){for(int i=1;i<=n;i++)//遍歷所有的起點(diǎn){for(int j=1;j<=n;j++)//遍歷所有的終點(diǎn){if (e[i][j]>e[i][k]+e[k][j])//如果當(dāng)前i->j的距離大于i->k->j的距離之和e[i][j]=e[i][k]+e[k][j];//更新從i->j的最短路徑}}} }算法思想:
①如果不允許有中轉(zhuǎn)點(diǎn),那么最短路徑就是我們的e[][]原始矩陣;
②現(xiàn)在只允許經(jīng)過1號(hào)頂點(diǎn)進(jìn)行中轉(zhuǎn),判斷e[i][1]+e[1][j]是否比e[i][j]要小,修改e[][];
③接下來只允許經(jīng)過1和2號(hào)頂點(diǎn)進(jìn)行中轉(zhuǎn)……
④最后,允許經(jīng)過1~n號(hào)所有頂點(diǎn)進(jìn)行中轉(zhuǎn),得到最后的e[][],就是要求的任意兩點(diǎn)之間的最短路程。
這里面是動(dòng)態(tài)規(guī)劃思想的體現(xiàn)。狀態(tài)轉(zhuǎn)移方程:e[i,j]=max{e[i,k]+e[k,j],e[i,j]};
算法過程:對(duì)于每一對(duì)頂點(diǎn) i 和 j,看看是否存在一個(gè)頂點(diǎn) k 使得從 i 到 k 再到 j 比已知的路徑更短。如果是,則更新它。
算法過程詳解:http://ahalei.blog.51cto.com/4767671/1383613
?
另外,特別鳴謝,本文參考(含大量例題):http://blog.csdn.net/hjd_love_zzt/article/details/26739593
?
作者:?AlvinZH
出處:?http://www.cnblogs.com/AlvinZH/
本人Github:https://github.com/Pacsiy/JobDu
本文版權(quán)歸作者AlvinZH和博客園所有,歡迎轉(zhuǎn)載和商用,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責(zé)任的權(quán)利.
轉(zhuǎn)載于:https://www.cnblogs.com/AlvinZH/p/6789912.html
總結(jié)
以上是生活随笔為你收集整理的四大算法解决最短路径问题(Dijkstra+Bellman-ford+SPFA+Floyd)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 论一只爬虫的自我修养
- 下一篇: Codeforces Round #24