Dijkstra演算法(內附c++程式碼)
阿新 • • 發佈:2020-11-01
Dijkstra是一種經典且高效的單源最短路徑演算法,其本質是一種貪心演算法,通過對圖向外層層擴充套件得到從某一給定點到所有其它點的最短路。
操作步驟
1. 記已找到最短路的點為集合S,未找到的點為集合Q,顯然初始狀態為除起點外的點都在集合Q中。
2. 將起點加入集合S中,同時更新集合Q中的點與起點的距離,與起點直接相連的點距離為邊權,不直接相連的點距離為正無窮,記dis[i]為i點與起點的距離。
3. 將集合Q中dis[]最小的點x取出,加入S集合,遍歷與x相鄰的點(將這些點記為y),更新這些點與起點的距離,dis[y]=min{dis[y],dis[x]+邊權[x][y]}(這一步即為對Q集合的更新) 。這一步是dijkstra的精髓
4. 重複進行步驟3,直到所有點都已加入S集合。
圖解(電腦自帶的畫圖畫的,就這樣吧。。)
我們以A為起點,模擬一下整個演算法的流程。
1. 將A點加入集合S,同時更新與A點直接相連的B,C距離,dis[B]=4,dis[C]=2,此時dis[D]=正無窮,dis[E]=正無窮。
2. 將dis最小的C點取出,加入集合S,同時更新與C點相鄰的點的距離,dis[D]=12,由於dis[C]+邊權[C][D]<dis[B],所以dis[B]=3。
3. 同上,此時dis最小的為B取出加入集合S,dis[E]=7。
4. 現在dis最小的為E,同上,dis[D]=10。
5. 現在dis最小的為D,此時所有點都已進入集合S,程式結束。
但dijkstra演算法是有侷限的,它只能用來處理邊權全部為正的圖,不能處理有負權邊的圖,因為這種情況下當一個點加入集合S後該點仍有可能通過負權邊來縮小dis。
以下為程式碼
#include<cstdio> #include<algorithm> #include<queue> #include<cstring> using namespace std; struct stu{ int to,next; long long int cap;//前向星存圖,以一點為起點,to為該邊連線的一個點,next為與該邊起點相同的下一條邊,cap為邊權 }; stu road[2000050]; int head[2000050],k=0,vis[1000050];//vis為集合S int n,m,s; long long int dis[1000050]; struct node { int u; long long d; bool operator<(const node& rhs) const { return d>rhs.d; } }; priority_queue<node>q;//用優先佇列來實現取出集合Q中dis最小值的點的功能 void add(int x,int y,long long int z) { road[k].to=y; road[k].next=head[x]; road[k].cap=z; head[x]=k; k++; } void dijkstra(int x) { for (int i=1;i<=n;i++) { dis[i]=2147483647; //開始所有點都沒有遍歷過,所以所有點與起點的距離初值為正無窮 } q.push((node){s,dis[s]}); dis[x]=0; while(q.empty()==0) { node uu=q.top(); int u=uu.u; q.pop(); if (vis[u]==1) { continue; } vis[u]=1;//將當前點加入集合S for (int i=head[u];i>-1;i=road[i].next) { int v=road[i].to; if (dis[v]>dis[u]+road[i].cap)//更新dis { dis[v]=dis[u]+road[i].cap; q.push((node){v,dis[v]}); } } } } int main() { memset(head,-1,sizeof(head)); scanf("%d%d%d",&n,&m,&s); for (int i=1;i<=m;i++) { int a,b; long long int c; scanf("%d%d%lld",&a,&b,&c); add(a,b,c); } dijkstra(s); for (int i=1;i<=n;i++) { printf("%lld ",dis[i]); } return 0; }