单源最短路径 Dijkstra+优先队列
2019-03-03 21:56
86 查看
用优先队列优化的Dijkstra
1找到最短距离已经确认的顶点,从它出发更新相邻顶点的最短距离
2此后不需要关心1中的“最短距离已经确认的顶点”
堆中元素共有O(V)个,更新和取出都有O(E)次,每次更新或取出堆的维护时间是O(logV),因此该算法的时间复杂度为O(ElogV).
[code]/* 7 10 1 2 2 1 3 5 2 3 4 3 4 2 2 4 6 4 6 1 2 5 10 5 6 3 5 7 5 6 7 9 0 2 5 7 12 8 17 */ #include<iostream> #include<vector> #include<queue> using namespace std; const int MAX_V=10005; const int INF=0x3f3f3f; struct edge{ int to;//边的终点 int cost;//权值 edge(int t,int c){ to=t; cost=c; } }; typedef pair<int,int> P;//first是最短距离,second是顶点编号 int V;//顶点数 int E;//边数 vector<edge> G[MAX_V]; int d[MAX_V]; void read(){ int start,to,cost; scanf("%d%d",&V,&E); for(int i=0;i<E;i++){ scanf("%d%d%d",&start,&to,&cost); G[start].push_back(edge(to,cost)); } } void dijkstra(int s){ //通过指定greater<P>参数,堆按照first从小到大的顺序取出值 priority_queue<P,vector<P>,greater<P> > que;//优先队列里存的都是最短距离已经确认的顶点 fill(d,d+V+1,INF); d[s]=0; que.push(P(0,s));//起点,起点到起点的最短距离是确定的(0) while(!que.empty()){ P p=que.top();que.pop();//每次取出容器中已经确定的离起点最近的点 printf("取出点%d\n",p.second); int v=p.second; if(d[v]<p.first)continue;//表示该点入队不只一次(即之前有多个点都可以到达它),那么d[v]也可能更新了不只一次, //而每次更新都会使d[v]更小,显然这个p.first是其中一次更新,但不是最小的那个,而最小的 //那个在此之前就出队了,所以不必再用这条记录继续往下更新了 for(int i=0;i<G[v].size();i++){//扫描其所有相邻的顶点,并更新他们的最短距离d[i] edge e=G[v][i]; if(d[e.to]>d[v]+e.cost){ printf("d[%d]:%d>d[%d]:%d+%d_to_%d_cost:%d\t更新d[%d]为%d\t%d入队\n",e.to,d[e.to],v,d[v],v,e.to,e.cost,e.to,d[v]+e.cost,e.to); d[e.to]=d[v]+e.cost;//d[v]是已经确定的 que.push(P(d[e.to],e.to));//更新后最短距离已经确认的点入队,优先队列里自动维护,队头是最小的那个 } } } } int main(){ read(); dijkstra(1); for(int i=1;i<=V;i++) printf("%d ",d[i]); return 0; }
下面为注释更详细的版本
[code]/* 7 10 1 2 2 1 3 5 2 3 4 3 4 2 2 4 6 4 6 1 2 5 10 5 6 3 5 7 5 6 7 9 0 2 5 7 12 8 17 */ #include<iostream> #include<vector> #include<queue> using namespace std; const int MAX_V=10005; const int INF=0x3f3f3f; struct edge{ int to;//边的终点 int cost;//权值 edge(int t,int c){ to=t; cost=c; } }; typedef pair<int,int> P;//first是该点入队时的最短距离,second是顶点编号 int V;//顶点数 int E;//边数 vector<edge> G[MAX_V]; int d[MAX_V]; void read(){ int start,to,cost; scanf("%d%d",&V,&E); for(int i=0;i<E;i++){ scanf("%d%d%d",&start,&to,&cost); G[start].push_back(edge(to,cost)); } } void dijkstra(int s){ //通过指定greater<P>参数,堆按照first从小到大的顺序取出值 priority_queue<P,vector<P>,greater<P> > que;//优先队列里存的都是最短距离已经确认的顶点 /* priority_queue 对于基本类型的使用方法相对简单。他的模板声明带有三个参数: priority_queue<Type, Container, Functional> 其中Type 为数据类型, Container 为保存数据的容器,Functional 为元素比较方式。 Container 必须是用数组实现的容器,比如 vector, deque 但不能用 list. STL里面默认用的是 vector. 比较方式默认用 operator< , 所以如果你把后面俩个参数缺省的话, 优先队列就是大顶堆,队头元素最大。 如果要用到小顶堆,则一般要把模板的三个参数都带进去。 STL里面定义了一个仿函数 greater<>,对于基本类型可以用这个仿函数声明小顶堆 p1 < p2; 两个pair对象间的小于运算,其定义遵循字典次序:如 p1.first < p2.first 或者 !(p2.first < p1.first) && (p1.second < p2.second) 则返回true。 pair详解 转https://blog.csdn.net/sevenjoin/article/details/81937695 */ fill(d,d+V+1,INF); d[s]=0; que.push(P(0,s));//起点,起点到起点的最短距离是确定的(0) while(!que.empty()){ P p=que.top();que.pop();//每次取出容器中已经确定的离起点最近的点 //printf("取出点%d\n",p.second); int v=p.second; if(d[v]<p.first)continue;//表示该点入队不只一次(即之前有多个点都可以到达它),那么d[v]也可能更新了不只一次, //而每次更新都会使d[v]更小,显然这个p.first是其中一次更新,但不是最小的那个,而最小的 //那个在此之前就出队了,所以不必再用这条记录继续往下更新了 /* 这里是队的取出情况 取出点1 d[2]:4144959>d[1]:0+1_to_2_cost:2 更新d[2]为2 2入队 d[3]:4144959>d[1]:0+1_to_3_cost:5 更新d[3]为5 3入队 取出点2 d[4]:4144959>d[2]:2+2_to_4_cost:6 更新d[4]为8 4入队 d[5]:4144959>d[2]:2+2_to_5_cost:10 更新d[5]为12 5入队 取出点3 d[4]:8>d[3]:5+3_to_4_cost:2 更新d[4]为7 4入队 取出点4 这个是P(7,4) d[6]:4144959>d[4]:7+4_to_6_cost:1 更新d[6]为8 6入队 取出点4 这个是P(8,4),而d[4]已经在第65行时被更新为7<p.first:8 所以continue 取出点6 d[7]:4144959>d[6]:8+6_to_7_cost:9 更新d[7]为17 7入队 取出点5 取出点7 */ for(int i=0;i<G[v].size();i++){//扫描其所有相邻的顶点,并更新他们的最短距离d[i] edge e=G[v][i]; if(d[e.to]>d[v]+e.cost){ //printf("d[%d]:%d>d[%d]:%d+%d_to_%d_cost:%d\t更新d[%d]为%d\t%d入队\n",e.to,d[e.to],v,d[v],v,e.to,e.cost,e.to,d[v]+e.cost,e.to); d[e.to]=d[v]+e.cost;//d[v]是已经确定的 que.push(P(d[e.to],e.to));//更新后最短距离已经确认的点入队,优先队列里自动维护,队头是最小的那个 } } } } int main(){ read(); dijkstra(1); for(int i=1;i<=V;i++) printf("%d ",d[i]); return 0; }
参考资料:挑战程序设计竞赛P102
相关文章推荐
- 【HDU 1874 2544 2066 2112】 Dijkstra单源最短路径专题 —— 优先队列+邻接表/邻接矩阵
- Dijkstra不能得到含有负权边图的单源最短路径
- 数据结构与算法--单源最短路径算法之dijkstra
- Dijkstra求单源最短路径(图,数据结构)
- 初学图论-Dijkstra单源最短路径算法基于优先级队列(Priority Queue)的实现
- (阶段三 dijkstra温习1.3)HDU 1874 畅通工程续(用dijkstra求单源起点和单点终点的最短路径(起点和终点动态给出))
- 贪心算法 - 单源最短路径 Dijkstra
- 算法导论-第24章- 单源最短路径 - 24.3 Dijkstra 算法
- Dijkstra单源最短路径(贪心选择)
- 算法 单源最短路径Dijkstra
- 单源最短路径问题[Dijkstra实现]
- 单源最短路径问题[Dijkstra实现]
- 单源最短路径---Dijkstra 算法--路径还原
- Dijkstra(迪杰斯特)算法求单源最短路径
- Dijkstra模板求单源点最短路径
- 贪心算法之最小生成树prim与单源最短路径dijkstra
- dijkstra 两点的最短路径 单源 最短路径
- 贪心算法之单源最短路径Dijkstra
- 单源最短路径---Dijkstra
- 6.2--Dijkstra单源最短路径