Dijkstra算法(求单源最短路径)
Dijkstra:
和Bellman-Ford算法都是求单源最短路径的,但Dijkstra算法要求图中没有负权边。
整体思路:集合S包含所有已经找到最短路径的节点,Q集合为没有找到最短路径的节点。
起初Q为V(原始顶点集),S为空集。每次从Q中选取节点v,v满足条件:s到v的路径权和最小。将v从Q中删除,加入S。
循环|V|次直到Q为空集,算法退出。
优化方式:
每次从Q中选取节点v需要O(N)时间。如果Q设计为优先队列,每次选取节点v只需要O(logN)时间。
struct EdgeNode{ //邻接链表节点 int num; int weigh; EdgeNode* next; EdgeNode():num(0),weigh(0),next(nullptr){} EdgeNode(int x,int w):num(x),weigh(w),next(nullptr){} }; struct vertex { int num; int dis; vertex(int x):num(x),dis(10000){} vertex():num(0),dis(10000){} }; struct cmp { bool operator ()(const vertex& x1,const vertex& x2){ return x1.dis>x2.dis; } }; //Dijkstra算法 void Dijkstra(vector<EdgeNode*>& vertexList,int s){ //起点s,邻接链表vertexList int vertexNum=vertexList.size(); priority_queue<vertex,vector<vertex>,cmp> p; //小顶堆,dis最小的节点排在队首 unordered_set<int> visited;//visited:已找到最短路径的顶点集合 vector<int> dis(vertexNum,10000);//dis[i]:s到i的最短路径权和 vector<int> pre(vertexNum,-1); //pre[i]:s到i的最短路径上i的前继节点 dis[s]=0; for(int i=0;i<vertexNum;++i){ if(i==s){ auto tmp=vertex(s); tmp.dis=0; p.push(tmp); } else{ p.push(vertex(i)); } } while(visited.size()<vertexNum){ auto u=p.top(); int uu=u.num; //uu为当前未找到最短路径的节点中dis最小的,将它加入集合 p.pop(); if(visited.count(uu)){ continue; } visited.insert(uu); auto node=vertexList[uu]; while(node!=nullptr){ int v=node->num; if(dis[v]>dis[uu]+node->weigh){ //用u-v边来松弛s-v的路径 dis[v]=dis[uu]+node->weigh; pre[v]=uu; auto tmp=vertex(v); tmp.dis=dis[v]; p.push(tmp); } node=node->next; } } }
代码未验证,仅供参考!
进击的小🐴农