最短路算法&模板
最短路問題是圖論的基礎問題。本篇隨筆就圖論中最短路問題進行剖析,講解常用的三種最短路算法:Floyd算法、Dijkstra算法及SPFA算法,並給出三種算法的模板。流暢閱讀本篇博客需要有圖論的基礎知識,了解什么是圖,什么是最短路,以及一些基本語法知識和算法基礎。
1、Floyd算法
我個人認為,Floyd算法是三種最短路算法中最簡單、最好理解的算法。它的適用范圍是任意兩點之間的最短路。這一點是其他兩種算法(單源最短路)無法比擬的。它的實現思路也很簡單:用三重循環,枚舉斷點、起始點和終點(注意:順序千萬不能反!!),如果起始點到斷點,斷點到終點的距離和小於起始點到終點當前狀態下的最短路(也就是說找到了一個比它還短的),那么就更新最短路。
它的優點就是簡潔明了,易於理解,但是缺點也顯而易見,通過它的實現途徑,我們可以發現,使用Floyd算法的題一定要用鄰接矩陣存圖,這樣的一個二維數組顯然對空間有着要求,一般來講,只能支持不超過500個點的圖,假如更多,便無法支持。同時,Floyd算法還對時間有着要求,因為是三重循環,所以它的時間復雜度是\(O(n^3)\)的,這樣的復雜度如果出現在一個復雜程序中,極其容易TLE,所以,請大家使用的時候,一定要讀題讀題,慎重慎重!
模板:
void Floyd()
{
memset(map,0x3f,sizeof(map));
for(int i=1;i<=n;i++)
map[i][i]=0;
for(int k=1;k<=n;k++)//順序不要反
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
map[i][j]=min(map[i][k]+map[k][j],map[i][j]);
}
2、Dijkstra算法
Dijkstra算法,中文名是迪傑斯特拉算法,簡寫是DIJ算法。DIJ算法是求解單源最短路,即從某一個源點到達其他所有點的最短路的一個經典算法。它的實現途徑也很好理解:我把點分成兩個集合,一個是已經掃描過的集合,另一個是沒有掃描的集合。即已經確定最短路和沒確定最短路的兩個集合,用v數組標記。然后我們從沒有掃描的集合中挑出一個dist最小的點放入已經掃描的集合中,然后從這個點開始搜索它的所有出邊,進行松弛操作即可。
DIJ算法的時間復雜度比較高,大約是\(O(n^2)\)級別的,同SPFA比確實是差了一些,但它有自己獨特的優勢,由於它的復雜度只和點有關,所以針對於稠密圖(邊多點少的圖),它的求解效率要比SPFA算法高很多。
關於DIJ算法可以使用二叉堆(優先隊列)進行優化,即我們常說的DIJ算法堆優化,由於篇幅較長,我放到了另一篇博客中進行講解,鏈接:
模板:
void dijkstra(int start)//源點
{
int temp,k,y;
memset(dist,0x3f,sizeof(dist));
memset(v,0,sizeof(v));
dist[start]=0;
for(int i=1;i<=n;i++)
{
temp=1<<30;
for(int j=1;j<=n;j++)
if(dist[j]<temp && v[j]==0)
k=j,temp=dist[j];
v[k]=1;
for(int j=head[k];j;j=nxt[j])
{
y=to[j];
if(dist[y]>dist[k]+val[j])
dist[y]=dist[k]+val[j];
}
}
}
3、SPFA算法
網上有一張熱圖,就是“關於SPFA,它死了”。但是SPFA並沒有死,它不僅活的好好的,還是一種國產算法,更是我最擅長的最短路算法。
SPFA的適用范圍同樣也是單源最短路,但是它的實現途徑較抽象:新建一個隊列,保存等待優化的節點,把源點加入隊列。取出隊首節點x,遍歷x的所有出邊,進行松弛操作,如果x點的到達點y被更新,且y不在當前隊列中,就把y壓入隊尾。重復直到隊列空為止。
SPFA的實現模板跟寬搜很類似,希望大家好好體會。
SPFA的時間復雜度也很優越,根據證明,期望的時間復雜度可以達到\(O(kM)\),其中M為邊數,k為所有頂點進隊的平均次數。一般來講,k<=2。
所以我們發現,SPFA算法適用於稀疏圖,即點特別多邊特別少的圖。這和DIJ算法形成了一個對比,希望大家能在做題的時候活學活用。
關於SPFA的算法,有兩種優化方式,由於篇幅較長,放在另一個博客中進行講解。鏈接如下:
模板:
void spfa(int start)
{
memset(dist,0x3f,sizeof(dist));
memset(v,0,sizeof(v));
queue<int> q;
q.push(start);
v[start]=1;
dist[start]=0;
while(!q.empty())
{
int x=q.front();
q.pop();
v[x]=0;
for(int i=head[x];i;=nxt[i])
{
int y=to[i];
if(dist[y]>dist[x]+val[i])
{
dist[y]=dist[x]+val[i];
if(v[y]==0)
q.push(y),v[y]=1;
}
}
}
}
