[最短路]使用優先佇列優化的Dijkstra演算法
阿新 • • 發佈:2018-12-24
用鄰接矩陣的Dijkstra演算法的程式碼:
int cost[RANGE][RANGE]; int d[RANGE]; bool used[RANGE]; int n,m; //頂點數,邊數 void Dijkstra( int s ) { int i,v,u; for( i=1; i<=n; ++i ) { used[i]=false; d[i]=cost[1][i]; } d[s]=0; while( true ) { v=-1; for( u=1; u<=n; ++u ) if( !used[u] && ( v==-1 || d[u]<d[v]) ) v=u; if( v==-1 ) break; used[v]=true; for( u=1; u<=n; ++u ) d[u]= min( d[u],d[v]+cost[v][u] ); } }
使用鄰接矩陣實現的dijkstra演算法的複雜度是O(V²)。使用鄰接表的話,更新最短距離只需要訪問每條邊一次即可,因此這部分的複雜度是O(E).但是每次要列舉所有的頂點來查詢下一個使用的頂點,因此最終複雜度還是O(V²)。在|E|比較小時,大部分的時間都花在了查詢下一個使用的頂點上,因此需要使用合適的資料結構進行優化。
需要優化的是數值的插入(更新)和取出最小值兩個操作,因此使用堆就可以了。把每個頂點當前的最短距離用堆來維護,在更新最短距離時,把對應的元素往根的方向移動以滿足堆的性質。而每次從堆中取出的最小值就是下一次要用的頂點。這樣堆中的元素共有O(V)個,更新和取出的操作有O(E)次,因此整個演算法的複雜度是O(ElogV)。
下面是使用STL的priority_queue實現。在每次更新時往堆裡插入當前最短距離和頂點的值對。插入的次數是O(E)次,當取出的最小值不是最短距離的話,就丟棄這個值。這樣整個演算法也可以在同樣的時間內完成。
struct edge {int to,cost;};
typedef pair<int,int> P; //first是最短距離,second是頂點的編號
int V;//頂點個數
vector<edge> G[MAXV];
int d[MAXV];
void dijkstra(int s)
{
priority_queue<P,vector<P>,greater<P> > que;
memset(d,INF,sizeof d);
d[s] = 0;
que.push(P(0,s)); //把起點推入佇列
while (!que.empty())
{
P p = que.top(); que.pop();
int v = p.second; //頂點的編號
if (d[v] < p.first) continue;
for(int i = 0; i < G[v].size(); i++)
{
edge e = G[v][i];
if (d[e.to] > d[v] + e.cost)
{
d[e.to] = d[v] + e.cost;
que.push(P(d[e.to],e.to));
}
}
}
}
相對於Bellman-Ford的O(VE)的複雜度,Dijkstra的複雜度是O(ElogV),可以更加高效地計算最短路的長度。不過需要注意的一點:當圖中存在負邊的情況下,Dijkstra演算法就無法正確求解問題,還是需要使用Bellman-Ford演算法。