淺談路徑規劃演算法之Dijkstra演算法
阿新 • • 發佈:2018-12-31
迪傑斯特拉(dijkstra)演算法是典型的用來解決最短路徑的演算法,也是很多教程中的範例,由荷蘭電腦科學家狄克斯特拉於1959年提出,用來求得從起始點到其他所有點最短路徑。該演算法採用了貪心的思想,每次都查詢與該點距離最的點,也因為這樣,它不能用來解決存在負權邊的圖。解決的問題大多是這樣的:有一個無向圖G(V,E),邊E[i]的權值為W[i],找出V[0]到V[i]的最短路徑。
演算法的步驟如下:
(1)初始化時,S只含有源節點;
(2)從U中選取一個距離v最小的頂點k加入S中(該選定的距離就是v到k的最短路徑長度);
(3)以k為新考慮的中間點,修改U中各頂點的距離;若從源節點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值是頂點k的距離加上k到u的距離;
(4)重複步驟(2)和(3),直到所有頂點都包含在S中。
下面舉個例子來說明這個過程,假設以A為源點。
dijkstra演算法實現的完整程式碼如下(已測試通過):
#include<iostream> #include<algorithm> #include<cmath> #include<string> #include<cstring> #include<cstdlib> #include<map> #include<set> #include<cstdio> #include<queue> #include<list> #include<vector> #include<stack> #include<deque> using namespace std; const int maxn=107;//點的個數 const int INF=0xfffffff;//不可達的距離 //邊的結構體 struct Edge { int from; int to; int dist; Edge(int u,int v,int d):from(u),to(v),dist(d) {} }; //迪傑斯特拉的結構體 struct Dijkstra { int n,m; vector<Edge> edges; //存邊 vector<int> G[maxn]; //存鄰接表 bool done[maxn]; //是否已經永久標號 int d[maxn]; //s到各個點的距離 int p[maxn]; //最短路上的一條弧 /*優先佇列預設最大堆,即從大到小排列, 而迪傑斯特拉演算法是尋找的最小路徑, 所以要選擇過載的<運算子,實現最小堆*/ struct HeapNode { int d,u; bool operator < (const HeapNode& rhs) const { return d>rhs.d; } }; //初始化,將鄰接表和邊的vector清空 void init(int n) { this->n=n; for(int i=0; i<n; i++) G[i].clear(); edges.clear(); } //加邊 void AddEdge(int from,int to,int dist) { /* 無向圖需要把這6行全加上 有向圖只需要加前三行 */ edges.push_back(Edge(from,to,dist)); m=edges.size(); G[from].push_back(m-1); //push的是下標,因為根據下標找到的資訊更全一些 edges.push_back(Edge(to,from,dist)); m=edges.size(); G[to].push_back(m-1); } //核心演算法,迪傑斯特拉演算法,S是源點 void dijkstra(int s) { priority_queue<HeapNode>Q; //最小堆 for(int i=0; i<n; i++) d[i]=INF; d[s]=0; memset(done,0,sizeof(done)); Q.push((HeapNode){0,s}); //建構函式,傳值給d,u,意思是某節點的距離和某節點 while(!Q.empty()) { HeapNode x=Q.top(); Q.pop(); int u=x.u; if(done[u]) continue; done[u]=true; for(int i=0; i<G[u].size(); i++) { Edge& e=edges[G[u][i]]; if(d[e.to]>d[u]+e.dist) { d[e.to]=d[u]+e.dist; p[e.to]=G[u][i]; Q.push((HeapNode){d[e.to],e.to}); } } } } }; int main() { freopen("out.txt","r",stdin); //開啟檔案讀取檔案 Dijkstra dij; int nodenum,edgenum,source; //節點數目、邊的數目、源點 int from,to,weight; //一條邊的起點、終點和權值 cin>>nodenum>>edgenum>>source; //初始化邊圖vector dij.init(nodenum); //加邊 for(int i=1; i<=edgenum; i++) { cin>>from>>to>>weight; dij.AddEdge(from,to,weight); } //搜尋 dij.dijkstra(source); //列印每個節點和源點的最短路 for(int i=0; i<nodenum; i++) { cout<<"源點到第"<<i<<"個點的最短路是:"<<dij.d[i]<<endl; } return 0; }
輸入:第一行三個數字分別是點的個數、邊的條數和源點,後邊9行是9條邊的起點、終點和權值
6 9 0
0 1 6
0 2 3
1 3 5
1 2 2
2 3 3
2 5 4
3 5 2
3 4 3
4 5 5
輸出:
源點到第0個點的最短路是:0
源點到第1個點的最短路是:5
源點到第2個點的最短路是:3
源點到第3個點的最短路是:6
源點到第4個點的最短路是:9
源點到第5個點的最短路是:7