dijkstra算法詳解(迪傑斯特拉算法)~~簡單易懂
PS:此算法不能用於求負權圖,要求所有邊的權重都為非負值。
一、簡介(百度百科)
迪傑斯特拉算法(Dijkstra)是由荷蘭計算機科學家狄克斯特拉於1959 年提出的,因此又叫狄克斯特拉算法。這是從一個頂點到其余各頂點的最短路徑算法,解決的是有權圖中最短路徑問題。迪傑斯特拉算法主要特點是從起始點開始,采用貪心算法的策略,每次遍歷到始點距離最近且未訪問過的頂點的鄰接節點,直到擴展到終點為止。
二、算法思想與原理
dijkstra算法思想是基於貪心算法思想的。所謂貪心算法即始終保持當前迭代解為當前最優解。意思就是在已知的條件下或是當前擁有的全部條件下保證最優解,若在此后的迭代中由於加入了新的條件使得產生了更優解則替代此前的最優解。通過不斷的迭代不斷保證每次迭代的結果都是當前最優解,那么當迭代到最后一輪時得到的就會是全局最優解。 由於下一輪迭代會參考上一輪的最優解,因此每一輪的迭代的工作量基本一致,降低了整體工作的復雜性。
在最短路徑的問題中,局部最優解即當前的最短路徑或者說是在當前的已知條件下起點到其余各點的最短距離。關鍵就在於已知條件,這也是Dijkstra算法最精妙的地方。我們來解釋一下。
對於Dijkstra算法,我們假設初始集合(也就是初始條件)不存在任何頂點的,即所有頂點之間是不存在任何路徑的,即我們認為所有頂點之間的距離都是無窮大。那么開始加入新的條件,因為我們已知源點距源點距離最小,所以加入進去,並加入它的邊,在該條件下,更新該源點到其余頂點的最短距離,選出沒有加入到已知集合的距源點距離最小的點,此點最短距離也被確定了(因為其他路徑都比這條路徑大,無法通過其他路徑間接到達這個頂點使得路徑更小),然后加入該點與其余還未加入已知條件頂點的邊,並以該點迭代刷新最短距離。再重復以上操作,直至所有頂點都加入已知條件集合。
三、具體步驟
- 選擇起點start與終點end;
- 所有點除起點外加入未知集合,並將起點加入已知集合,即至標志位為真,意為已確定該點到源點的最短路徑;
- 初始化計算,更新起點與其他各點的耗費dis(start,n);
- 在未知集合中,選擇dis(start,n)中值最小的點x,將x加入已知集合。
- 對於剩余頂點中,計算dis(start,n)>dis(start,x)+dis(x,n)
若真則dis(start,n)=dis(start,x)+dis(x,n),此時start與n點路徑經過x點。循環直至goal點加入已知列表,取得dis(start,goal)即為最短距離。
四、動態展示
五、一般代碼實現(以鄰接矩陣為例)
- 基本數據
const int inf=0x3f3f3f3f; //代表無窮大。
const int maxn=100;//最大頂點數
int n,m;//n個頂點,m條邊。
bool visited[maxn];//判斷是否確定到源點的最終最短距離。
int graph[maxn][maxn];//帶權圖
int dis[maxn];//頂點到源點的最短距離。
int start,goal;//起點與目標點。
- 初始化
void init(){
memset(visited,false,sizeof(visited));
for(int i=1;i<=n;i++){
dis[i]=graph[start][i];//初始化dis數組。
}
}
- dijkstra算法核心
void dijkstra(){
//源點為源點start。
int minn;//記錄每趟最短路徑中最小的路徑值。
int pos;//記錄得到的minn所對應的下標。
init();//調用初始化函數。
visited[start]=true;
for(int i=1;i<=n;i++){
//將n個頂點依次加入判斷。
minn=inf;
for(int j=1;j<=n;j++){
if(!visited[j]&&dis[j]<minn){
minn=dis[j];
pos=j;
}
}
//經過這趟for循環后我們找到的就是我們想要的點,可以確定這點到源點的最終最短距離了。
visited[pos]=true;//我們將此點並入已知集合。
//接下來就是更新dis數組了,也就是當前最短距離,這都是針對還沒有並入已知集合的點。
for(int j=1;j<=n;j++){
if(!visited[j]&&dis[j]>dis[pos]+graph[pos][j])
dis[j]=dis[pos]+graph[pos][j];
}
}
//退出循環后,所有的點都已並入已知集合中,得到的dis數組也就是最終最短距離了。
cout<<dis[goal]<<endl;//輸出目標點到源點的最短路徑長度。
}
- 主函數與頭文件等
#include<bits/stdc++.h>
using namespace std;
int main()
{
while(cin>>n>>m){
memset(graph,inf,sizeof(graph));
int u,v,w;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
// graph[u][v]=w;//有向圖
graph[u][v]=graph[v][u]=w;//無向圖
}
cin>>start>>goal;//輸入起點與終點。
dijkstra();
}
return 0;
}
六、拓展
如果你學會了dijkstra,那恭喜你成功突破了一關。但是,沒有優化的dijkstra算法時間復雜度為 O ( n 2 ) O(n^2) O(n2),如果頂點很多邊很少呢等等卡鄰接矩陣的題,那么建議你還是要學一下dijkstra的優化版了。詳情點擊:Dijkstra算法優化~~你一定可以看懂的四種進階優化