1. 程式人生 > >經典演算法——迪傑斯特拉(Dijkstra)最短路徑

經典演算法——迪傑斯特拉(Dijkstra)最短路徑

基本思想

迪傑斯特拉演算法是由荷蘭電腦科學家狄克斯特拉於1959 年提出的,因此又叫狄克斯特拉演算法。是從一個頂點到其餘各頂點的最短路徑演算法,解決的是有向圖中最短路徑問題。迪傑斯特拉演算法主要特點是以起始點為中心向外層層擴充套件,直到擴充套件到終點為止。

其基本思想是,設定頂點集合S並不斷地作貪心選擇來擴充這個集合。一個頂點屬於集合S當且僅當從源到該頂點的最短路徑長度已知。

初始時,S中僅含有源。設u是G的某一個頂點,把從源到u且中間只經過S中頂點的路稱為從源到u的特殊路徑,並用陣列dist記錄當前每個頂點所對應的最短特殊路徑長度。Dijkstra演算法每次從V-S中取出具有最短特殊路長度的頂點u,將u新增到S中,同時更新陣列dist(算導中稱作Relax)。一旦S包含了所有V中頂點,dist就記錄了從源到所有其它頂點之間的最短路徑長度。

虛擬碼

Dijkstra(G,w,s)
1. Initializ_single_source(G,s) //dist[] = INF, dist[s] = 0
2. S = 空集
3. Q=G.V
4. while Q != 空集
u = Extract-Min(Q)
S = SU{u}
for each vertex v屬於G.adj[u]
Relax(u,v,w)

C++實現

#include<iostream>
#include<vector>
#include<stack>
//#include<limits.h> using namespace std; struct edge{ int noEnd; //邊的終點序號 int w; edge(int _no, int _w):noEnd(_no),w(_w){} }; class Graph{ private: vector<vector<edge>> Edge; //鄰接表 vector<int> dist; vector<char> vertex; // 頂點資訊 int n; // 頂點個數 int
e; //邊數 vector<int> path; //記錄著從源點v0到當前點最短路徑的前驅節點序號 public: Graph(int _n, int _e):n(_n),e(_e){ vertex.resize(n+1); //計數序號從1開始 Edge.resize(n+1); path.resize(n+1); dist.resize(n+1); //初始化 for(int i=1; i<=n; i++){ path[i] = -1; dist[i] = INT_MAX; } } //依次讀入頂點資訊 void readVertex(){ cout<<"請依次輸入頂點資訊:"<<endl; for(int i=1; i<=n; i++) cin>>vertex[i]; } //依次讀入邊資訊 void readEdge(){ cout<<"請依次輸入邊資訊source,dst,weight:"<<endl; int r,d,w; //r、d、w分別表示起點、終點、權重 for(int i=1; i<=e; i++){ cin>>r>>d>>w; Edge[r].push_back(edge(d,w)); // Edge[d].push_back(edge(r,w)) 無向圖 } } void Dijkstra(int v0); // v0是源點 void printPath(int v, int v0){ stack<int> s; while(v != v0){ s.push(v); v = path[v]; } cout<<endl<<vertex[v0]; while(!s.empty()){ v = s.top(); s.pop(); cout<<"->"<<vertex[v]; } cout<<endl; } }; void Graph::Dijkstra(int v0){ dist[v0] = 0; vector<bool> isVisited(n+1,false); isVisited[v0] = true; for(int i=0; i<Edge[v0].size(); i++){ edge tmp = Edge[v0][i]; if(dist[v0] + tmp.w < dist[tmp.noEnd]){ dist[tmp.noEnd] = dist[v0] + tmp.w; path[tmp.noEnd] = v0; } } int N=n-1; while(N--){ //找到下一個加入S集合中的點 int u = -1; int tmpMin = INT_MAX; for(int i=1; i<=n; i++){ if(!isVisited[i] && dist[i] < tmpMin){ u = i; tmpMin = dist[i]; } } isVisited[u] = true; //Relax 更新dist for(int i=0; i<Edge[u].size(); i++){ edge tmp = Edge[u][i]; if(dist[u] + tmp.w < dist[tmp.noEnd]){ dist[tmp.noEnd] = dist[v0] + tmp.w; path[tmp.noEnd] = u; } } } } int main(){ int n,e; cout<<"請輸入頂點數,邊數:"<<endl; cin>>n>>e; Graph graph(n,e); graph.readVertex(); graph.readEdge(); int v0, v; cout<<"請輸入源點及終點序號:"<<endl; cin>>v0>>v; graph.Dijkstra(v0); graph.printPath(v,v0); cout<<endl; }