迪杰斯特拉算法详解
簡(jiǎn)述
迪杰斯特拉算法是一種基于貪心法求有向圖或無向圖單源最短路的算法,其本質(zhì)就是把頂點(diǎn)集劃分為兩部分,已求出最短路徑的集合S和未求出最短路徑的集合U,U集里面每個(gè)點(diǎn)都有一個(gè)邊權(quán),代表源點(diǎn)通過S集里的點(diǎn)到達(dá)U集的那個(gè)點(diǎn)的最短路徑(注意這里的最短不一定是全局最短),S一開始只有源點(diǎn),U里面和源點(diǎn)的邊權(quán)為路徑本身,不相鄰的邊權(quán)為inf,通過貪心不斷地把U集合里面的頂點(diǎn)加入S,直到求完源點(diǎn)到所有頂點(diǎn)的最短路徑。暴力時(shí)間復(fù)雜度為O(n方),經(jīng)過堆優(yōu)化可為O(nlogn)。
思想過程
如何將集合U里的點(diǎn)一個(gè)一個(gè)加入集合S呢?
我們發(fā)現(xiàn),我們可以確定,U里面頂點(diǎn)的最小邊權(quán)就是源點(diǎn)到該路徑的最短路徑。
例如該圖:
A和B直接和源點(diǎn)相鄰,那么U集中最小邊權(quán)對(duì)應(yīng)的頂點(diǎn)就是源點(diǎn)到該頂點(diǎn)的最短路徑,在此圖就是源點(diǎn)到B點(diǎn)的最短路徑為2,因?yàn)锳點(diǎn)的邊權(quán)5不是最小邊權(quán),所以在這一步還不能確定是不是最短路徑。那為什么U集里的最小邊權(quán)就是源點(diǎn)到該點(diǎn)的最短路徑呢?我們用反證法,假設(shè)最小邊權(quán)不是源點(diǎn)到該點(diǎn)設(shè)為y的最小路徑,那么必然存在一個(gè)點(diǎn)設(shè)為x,使得源點(diǎn)到x的距離加x到y(tǒng)的距離小于U里的最小邊權(quán),那么源點(diǎn)到x的距離就要小于這個(gè)最小邊權(quán),矛盾!所以結(jié)論得證!
在加入S集后,該點(diǎn)要對(duì)其他點(diǎn)的邊權(quán)進(jìn)行松弛,什么意思呢?U集合里面的邊權(quán)是源點(diǎn)到該頂點(diǎn)的路徑距離嘛,一開始只有和源點(diǎn)相鄰的邊,但隨著S集合新點(diǎn)的加入,源點(diǎn)到其他點(diǎn)的距離會(huì)被改變,因?yàn)镾集合有新的點(diǎn)x加入,那么源點(diǎn)到其他的點(diǎn)的距離可能會(huì)被更小的源點(diǎn)到x的距離加x到其他點(diǎn)的距離取代,即如上圖:B加入S之后,U集里面A的邊權(quán)就可以更新為 源點(diǎn)-B+B-A,為4,4比5小,故更新。所以每當(dāng)有點(diǎn)加入S集的時(shí)候,都要對(duì)U里面的其他點(diǎn)的邊權(quán)進(jìn)行松弛。
于是不斷地加入,松弛,加入,松弛,直到全部點(diǎn)都在S里面,就能跑出源點(diǎn)S到其余所有點(diǎn)的最短路徑了。
算法模擬
因?yàn)镾集和U集沒有相同的部分,所以我們用一個(gè)集合加個(gè)標(biāo)記就可以區(qū)分兩個(gè)集合。
以此圖為例,vis[i]為1代表i點(diǎn)加入了S集,設(shè)起點(diǎn)為D,對(duì)應(yīng)表如下:
?剛開始S集只有D點(diǎn),初始化D點(diǎn)自己到自己的最短路為0,與D點(diǎn)直接相鄰的只有C和E
?現(xiàn)在將dis里最小的邊權(quán)加到S里->D到C的最短距離為3,同時(shí)松弛其他點(diǎn)
?因?yàn)镃的加入使B和F點(diǎn)可達(dá),松弛后D到B和F的距離為13和9,然后選擇邊權(quán)最小的E加入S集
?E的加入松弛了F點(diǎn)和G點(diǎn),同理現(xiàn)在將F加入S集
?F的加入松弛了A點(diǎn),現(xiàn)在最小邊權(quán)的點(diǎn)是G,將G加入S集
?G的加入并未能松弛其他點(diǎn),此時(shí)將B加入S集
?B點(diǎn)的加入也并未能松弛其余點(diǎn),最后將A點(diǎn)加入S集
經(jīng)過上述操作,我們把所有點(diǎn)都加進(jìn)S集合里了,最后得出的dis[i]就是源點(diǎn)D到i點(diǎn)的最短距離!
算法的正確性證明
U集邊權(quán)的本質(zhì)就是源點(diǎn)通過干S集里面的點(diǎn)到達(dá)目標(biāo)點(diǎn)的最短路徑長(zhǎng)度,證明迪杰斯特拉算法就是要證明U集合里的最小邊權(quán)就是全局最短路徑,所有的操作都是基于這個(gè)結(jié)論的,最小邊權(quán)是最短路徑,加入S集,因?yàn)镾集更新了所以U集的邊權(quán)肯會(huì)變,然后重復(fù)。
那么這個(gè)結(jié)論如何證明呢?
設(shè)源點(diǎn)為u,現(xiàn)有一點(diǎn)v屬于U集且該點(diǎn)權(quán)值最小,設(shè)為x,我們使用反證法,假設(shè)x不是全局最短路徑,那必然存在另一點(diǎn)k不屬于S集,使得u到k+k到v小于u到v。那么u到k的權(quán)值必然小于u到v,那么u到v的權(quán)值就不是U集里面最小的了,應(yīng)該是k才對(duì),矛盾,命題得證!
代碼詳解
代碼實(shí)現(xiàn)分為三部,找最短邊權(quán)點(diǎn)及其權(quán)值,將它加入S集,松弛其他點(diǎn),在找最小邊權(quán)的地方復(fù)雜度最大,所以總復(fù)雜度為O(n方)
int m[maxn][maxn],d[maxn],vis[maxn];
void dij(int u,int n){//起點(diǎn)為u,總頂點(diǎn)數(shù)為n
d[u]=0;//自己到自己就是0
for(int i=1;i<=n;i++){
int tmp=inf,k=1;
for(int j=1;j<=n;j++){
if(vis[j]==0&&d[j]<tmp){
k=j;
tmp=d[j];
}
}//此時(shí)tmp就是U集中最小的邊權(quán),k就是對(duì)應(yīng)的頂點(diǎn)
vis[k]=1;//k點(diǎn)加入S集
for(int j=1;j<=n;j++){
if(vis[j]==0&&tmp+m[k][j]<d[j]){//因?yàn)閗的加入,可以松弛U的其他點(diǎn)
d[j]=tmp+m[k][j];
}
}
}
}
模板
int m[maxn][maxn],d[maxn],vis[maxn];
void dij(int u,int n){
d[u]=0;
for(int i=1;i<=n;i++){
int tmp=inf,k=1;
for(int j=1;j<=n;j++){
if(vis[j]==0&&d[j]<tmp){
k=j;
tmp=d[j];
}
}
vis[k]=1;
for(int j=1;j<=n;j++){
if(vis[j]==0&&tmp+m[k][j]<d[j]){
d[j]=tmp+m[k][j];
}
}
}
}
暴力n方
總結(jié)
- 上一篇: allure标记用例级别severity
- 下一篇: ftp服务及配置