再谈Dijkstra算法和堆优化
来源:互联网 发布:java集合类原理 编辑:程序博客网 时间:2024/05/18 02:28
Orz,今天和Java老师讨论到了图的遍历,然后扩展到最短路。感觉现场在黑板写还是有点紧张,大脑一有点懵,本身自己说话就有点口吃,好多都没表达出来。毕竟是现场发挥。。。。 感觉如果以后面试的时候这样面对HR肯定会爆炸,所以一定要抽下时间好好回顾一下。自己汉语都说不流利,还学毛线的外语?
下面再回顾下dij算法思路和代码:
如上图,从点A->点F,最短路径为A->C->D->F,Min=3+3+3=9
首先Java老师是我认为讲的最干练的一位老师了,虽然课时有限,我之前搞过一段时间Java,感觉以前遇到的重难点老师都点到了。而其他小枝末节让我们自己下来实践。
老师一直强调了dfs(深度优先遍历)的重要性,确实,dfs在图论中一直占据着重要的角色。可扩展到图论中割顶,桥,拓扑,双连通分量,强连通分量问题的解决上。在做一些算法题目的过程中,深搜感觉可以解决大部分搜索类题目了,但是深搜难在剪枝,剪枝可以减少大量不必要的搜索过程。这部分我自己做的不够好。投入时间首先都没达到。
用邻接矩阵的Dijkstra算法的代码:
int mp[maxn][maxn];int dis[maxn];bool visit[maxn];int n,m; //V,E void Dijkstra( int s ) { int i,v,u; for( i=1; i<=n; ++i ) { visit[i]=false; dis[i]=mp[1][i]; } dis[s]=0; while( true ) { v=-1; for( u=1; u<=n; ++u ) if( !visit[u] && ( v==-1 || dis[u]<dis[v]) ) v=u; if( v==-1 ) break; visit[v]=true; for( u=1; u<=n; u++ ) dis[u]= min( dis[u],dis[v]+mp[v][u] ); }}
Dij算法是基于广搜,松弛的时候有点贪心和动态规划的思想。
使用邻接矩阵实现的dijkstra算法的复杂度是O(V²)。使用邻接表的话,更新最短距离只需要访问每条边一次即可,因此这部分的复杂度是O(E).但是每次要枚举所有的顶点来查找下一个使用的顶点,因此最终复杂度还是O(V²)。在|E|比较小时,大部分的时间都花在了查找下一个使用的顶点上,因此需要使用合适的数据结构进行优化。
优先队列+dijkstra算法:
总时间复杂度=找最短距离 u := vertex in Q with min dist[u] 的时间复杂度 +
更新距离 dist[v] := min{dist[v],dist[u] + length(u, v)} 的时间复杂度
对于一个无向图G(V,E)来说,
找最短距离的时间复杂度为O(|V|*|V|)(共循环V次,每次V个点),考虑到Q每次递减1,实际复杂度为O(|V|^2/2);
由于图共有E条边,每条边最多被更新2次(1条边2个端点),因此更新距离的时间复杂度为O(2*|E|)。
因此,总时间复杂度=O(2*|E|+|V|^2/2)
然后,实际情况中经常会遇到 |V|^2>>|E| 的稀疏图,即O(2*|E|+|V|^2/2)=O(|V|^2/2)~
因此,如果我们能够优化 findMIN部分,即可大大优化稀疏图下的dijkstra算法~
findMIN的部分优化方法很多,最简单的就是用二分搜索O(logN)代替线性搜索 O(N)~
这里我们将集合Q转化成一个优先队列(priority queue),这样findMIN的时间复杂度变成了O(1),而每次更新priority queue需要花费O(log|V|)~
综上,采用优先队列之后,总时间复杂度=O(2*|E|+|V|*log|V|),
这样的优化对于稀疏图(|V|^2>>|E|)来说,尤为有效~
堆的实现原理这里就不说了,在很多书里面都有详细介绍。
下面是使用STL的priority_queue实现。在每次更新时往堆里插入当前最短距离和顶点的值对。
#include <iostream> #include <cstdio> #include <queue> #include <vector> using namespace std; const int Ni = 10000; const int INF = 1<<27; struct node{ int x,d; node(){} node(int a,int b){x=a;d=b;} bool operator < (const node & a) const { if(d==a.d) return x<a.x; else return d > a.d; } }; vector<node> eg[Ni]; int dis[Ni],n; void Dijkstra(int s) { int i; for(i=0;i<=n;i++) dis[i]=INF; dis[s]=0; //用优先队列优化 priority_queue<node> q; q.push(node(s,dis[s])); while(!q.empty()) { node x=q.top();q.pop(); for(i=0;i<eg[x.x].size();i++) { node y=eg[x.x][i]; if(dis[y.x]>x.d+y.d) { dis[y.x]=x.d+y.d; q.push(node(y.x,dis[y.x])); } } } } int main() { int a,b,d,m; while(scanf("%d%d",&n,&m),n+m) { for(int i=0;i<=n;i++) eg[i].clear(); while(m--) { scanf("%d%d%d",&a,&b,&d); eg[a].push_back(node(b,d)); eg[b].push_back(node(a,d)); } Dijkstra(1); printf("%d\n",dis[n]); } return 0; } /* 6 6 1 2 2 3 2 4 1 4 5 2 5 2 3 6 3 5 6 3 */
最后一点,dij算法不能解决负权值问题。还是需要使用Bellman-Ford算法或者SPFA算法。
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 再谈Dijkstra算法和堆优化
- 堆优化的Dijkstra算法
- hdu2112 dijkstra算法+堆优化
- Dijkstra算法及其堆优化
- 最短路算法 堆优化 dijkstra+heap
- Dijkstra算法+堆优化(模板)
- dijkstra算法+堆优化 ///各种被坑~~~
- Dijkstra算法--邻接表存储+堆优化
- 堆优化的Dijkstra
- DIJKSTRA堆优化
- Dijkstra + 堆优化
- 堆优化Dijkstra模版
- Dijkstra+堆优化
- 堆优化Dijkstra
- 堆优化Dijkstra
- Dijkstra + 堆优化
- Dijkstra+堆优化 模板
- Dijkstra堆优化
- C#通过读取配置文件里的内容连接数据库
- 我对于iOS的KVO的理解基于Objective-C
- 【Lucene&&Solr】Windows搭建solr服务器
- 伪类巧用(1)
- spring登陆简单小案例
- 再谈Dijkstra算法和堆优化
- c++实验五:求和
- Eclipse git的使用
- 一、myeclipse整合Gradle插件进行开发
- Oracle 12.2新特性掌上手册
- html中的3列布局研究
- CodeForces 612D
- APP安全报告第二十期:手游开发商需重视安全,延长手游的生命周期!
- python 命名规范