經典演算法——迪傑斯特拉(Dijkstra)最短路徑
阿新 • • 發佈:2018-12-27
基本思想
迪傑斯特拉演算法是由荷蘭電腦科學家狄克斯特拉於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;
}