標籤:blog 思想 可以轉化 包括 amp 修改 i++ 空間 cost
Dijkstra演算法思想
Dijkstra演算法思想為:設G=(V,E)是一個帶權有向圖(無向可以轉化為雙向有向),把圖中頂點集合V分成兩組,第一組為已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以後每求得一條最短路徑 , 就將 加入到集合S中,直到全部頂點都加入到S中,演算法就結束了),第二組為其餘未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點v到S中各頂點的最短路徑長度不大於從源點v到U中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點為中間頂點的當前最短路徑長度。
Dijkstra演算法具體步驟
(1)初始時,S只包含源點,即S={v},v的距離dist[v]為0。U包含除v外的其他頂點,U中頂點u距離dis[u]為邊上的權值(若v與u有邊) )或∞(若u不是v的出邊鄰接點即沒有邊<v,u>)。
(2)從U中選取一個距離v(dist[k])最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。
(3)以k為新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u(u∈ U)的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權(即如果dist[k]+w[k,u]<dist[u],那麼把dist[u]更新成更短的距離dist[k]+w[k,u])。
(4)重複步驟(2)和(3)直到所有頂點都包含在S中(要迴圈n-1次)
Dijkstra演算法實現
直接實現
最簡單的實現方法就是,在每次迴圈中,再用一個迴圈找距離最短的點,然後用任意的方法更新與其相鄰的邊,時間複雜度顯然為O(n2)
對於空間複雜度:如果只要求出距離,只要n的附加空間儲存距離就可以了(距離小於當前距離的是已訪問的節點,對於距離相等的情況可以比較編號或是特殊處理一下)。如果要求出路徑則需要另外V的空間儲存前一個節點,總共需要2n的空間。
/********************************* * 最短路徑---Dijkstra演算法實現 * HDU:2544 * BLOG:www.cnblogs.com/newwy * AUTHOR:Wang Yong **********************************/ #include <iostream> #define MAX 100 #define INF 1000000000 using namespace std; int dijkstra (int mat[][MAX],int n, int s,int f) { int dis[MAX]; int mark[MAX];//記錄被選中的結點 int i,j,k = 0; for(i = 0 ; i < n ; i++)//初始化所有結點,每個結點都沒有被選中 mark[i] = 0; for(i = 0 ; i < n ; i++)//將每個結點到start結點weight記錄為當前distance { dis[i] = mat[s][i]; //path[i] = s; } mark[s] = 1;//start結點被選中 //path[s] = 0; dis[s] = 0;//將start結點的的距離設定為0 int min ;//設定最短的距離。 for(i = 1 ; i < n; i++) { min = INF; for(j = 0 ; j < n;j++) { if(mark[j] == 0 && dis[j] < min)//未被選中的結點中,距離最短的被選中 { min = dis[j] ; k = j; } } mark[k] = 1;//標記為被選中 for(j = 0 ; j < n ; j++) { if( mark[j] == 0 && (dis[j] > (dis[k] + mat[k][j])))//修改剩餘結點的最短距離 { dis[j] = dis[k] + mat[k][j]; } } } return dis[f]; } int mat[MAX][MAX]; int main() { int n,m; while(scanf("%d %d",&n,&m)) { int a,b,dis; if(n == 0 || m == 0) break; int i,j; for(i = 0 ; i < n;i++) for(j = 0 ; j < n; j++) mat[i][j] = INF; for(i = 0 ; i < m ;i++) { scanf("%d %d %d",&a,&b,&dis); --a,--b; if(dis < mat[a][b] || dis < mat[b][a]) mat[a][b] = mat[b][a] = dis; } int ans = dijkstra(mat,n,0,n-1); printf("%d\n",ans); } }
二元堆積實現
使用鄰接矩陣實現的dijkstra演算法的複雜度是O(V2)。使用鄰接表的話,更新最短距離只需要訪問每條邊一次即可,因此這部分的複雜度是O(E).但是每次要枚舉所有的頂點來尋找下一個使用的頂點,因此最終複雜度還是O(V2)。在|E|比較小時,大部分的時間都花在了尋找下一個使用的頂點上,因此需要使用合適的資料結構進行最佳化。
需要最佳化的是數值的插入(更新)和取出最小值兩個操作,因此使用堆就可以了。把每個頂點當前的最短距離用堆來維護,在更新最短距離時,把對應的元素往根的方向移動以滿足堆的性質。而每次從堆中取出的最小值就是下一次要用的頂點。這樣堆中的元素共有O(V)個,更新和取出的操作有O(E)次,因此整個演算法的複雜度是O(ElogV)。
下面是使用STL的priority_queue實現。在每次更新時往堆裡插入當前最短距離和頂點的值對。插入的次數是O(E)次,當取出的最小值不是最短距離的話,就丟棄這個值。這樣整個演算法也可以在同樣的時間內完成。
#include <bits/stdc++.h> using namespace std; /* * 使用優先隊列最佳化Dijkstra演算法 * 複雜度O(ElogE) * 注意對vector<Edge>E[MAXN]進行初始化後加邊 */ const int INF=0x3f3f3f3f; const int MAXN=1000010; struct qnode { int v; int c; qnode(int _v=0,int _c=0):v(_v),c(_c){} bool operator <(const qnode &r)const { return c>r.c; } }; struct Edge { int v,cost; Edge(int _v=0,int _cost=0):v(_v),cost(_cost){} }; vector<Edge>E[MAXN]; bool vis[MAXN]; int dist[MAXN]; void Dijkstra(int n,int start)//點的編號從1開始 { memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++)dist[i]=INF; priority_queue<qnode>que; while(!que.empty())que.pop(); dist[start]=0; que.push(qnode(start,0)); qnode tmp; while(!que.empty()) { tmp=que.top(); que.pop(); int u=tmp.v; if(vis[u])continue; vis[u]=true; for(int i=0;i<E[u].size();i++) { int v=E[tmp.v][i].v; int cost=E[u][i].cost; if(!vis[v]&&dist[v]>dist[u]+cost) { dist[v]=dist[u]+cost; que.push(qnode(v,dist[v])); } } } } void addedge(int u,int v,int w) { E[u].push_back(Edge(v,w)); } int main() { int n,m; int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)E[i].clear(); for(int i=0;i<m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); //addedge(v,u,w);無向圖 } Dijkstra(n,1); //單源最短路,dist[i]為從源點start到i的最短路 } return 0; }
Dijkstra最短路演算法