最短路径算法汇总
2016-05-17 13:43
344 查看
Dijkstra
d[0]=0;d[1~n]=INF;
1选最小的d[i];
2从i点出发所有边 松弛d[j]
伪代码:
for(1~n)
{
所有未标记的点选出d最小的点x;
标记x点走过;
i点出发所有边 更新d[y]=min(d[y],d[x]+w[x][y]); // <x,y>属于E;
}
1)简易版:O(n*n)
对边编号:u[i],v[i] 表示第i边连通哪两点 v[i]为i点标记;
打印路径:将 for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]);
换为 for(int y=0;y<n;y++) if(d[y]>d[x]+w[x][y]) d[y]=d[x]+w[x][y],path[y]=x;
2)优化版 O(m*logn)
对点编号 vector<int> G[maxn]; 保存邻接表;
优先队列取出d[i]最小的,就不用O(n)找出d[i]最小的了;
Bellman-Ford
对每个已更新过的节点都更新其相连的节点;
最短路最多经过n-1点(起点不算),经过m条边;
伪码描述
for(1~n-1) //最多检查n-1次
for(1~m) //每次每条边都要检查
{
if(x点已经更新) 更新周围的点;
}
因为Dijkstra是每次取出最小d[i],当存在负权就会出现问题;
而使用Bellman-Ford只是随意取出相邻边所以不会出现问题;
但正是因为是随意取出相邻边,所以对于每个节点要多次检查;
即 每个节点可以多次进入并检查该节点是否能更新 ,直到无法更新周围点;
对于起点i,其他的点都要扫一遍n-1,对于每一点相邻的边都要走一遍m
有三点要注意
1)每次for循环e.to只能压入队列一次,所以要用inq标识 ;
2)每个节点最多要放入队列n次(因为每个节点至少放入队列一次,若每个节点都可连通到x点,那么x点要进入队列n次)
所以cnt[e.to]>n 表明无最短路,可能存在负环;
3)每当e.to被更新过,就要放入队列更新e.to相连的点,直到所有的点都更新完,算法结束;
Floyd 多源最短路径 O(n^3)
通过一个图的权值矩阵求出它的每两点间的最短路径矩阵
i~j 要经过k点
d[0]=0;d[1~n]=INF;
1选最小的d[i];
2从i点出发所有边 松弛d[j]
伪代码:
for(1~n)
{
所有未标记的点选出d最小的点x;
标记x点走过;
i点出发所有边 更新d[y]=min(d[y],d[x]+w[x][y]); // <x,y>属于E;
}
1)简易版:O(n*n)
对边编号:u[i],v[i] 表示第i边连通哪两点 v[i]为i点标记;
memset(v,0,sizeof(v)); for(int i=0;i<n;i++) for(int j=0;j<n;j++) { int x,m=INF; for(int y=0;y<n;y++) if(!v[y]&&m>=INF) m=d[y],x=y; //选出最小的d[x]; v[x]=1; for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]); //更新d[y]; }
打印路径:将 for(int y=0;y<n;y++) d[y]=min(d[y],d[x]+w[x][y]);
换为 for(int y=0;y<n;y++) if(d[y]>d[x]+w[x][y]) d[y]=d[x]+w[x][y],path[y]=x;
2)优化版 O(m*logn)
对点编号 vector<int> G[maxn]; 保存邻接表;
优先队列取出d[i]最小的,就不用O(n)找出d[i]最小的了;
struct Edge { int from,to,dist; Edge(int u,int v,int w) : from(u),to(v),dist(w) {} }; struct HeapNode { int d,u; bool operator < (const HeapNode& rhs) const { return d<rhs.d; } } vector<int> G[maxn]; //G[u][i] 表示u点连接的边的编号; vector<Edge> edges; //保存对应边的编号的一切信息 bool vis[maxn]; // int d[maxn],path[maxn]; void dijkstra(int s) { priority_queue<HeadNode> Q; Q.push(HeadNode(0,s)); for(int i=0;i<n;i++) d[i]=INF,vis[i]=false; d[s]=0; while(!Q.empty()) { HeapNode x=Q.top();Q.pop(); int u=x.u; if(vis[u]) continue; vis[u]=true; for(int i=0;i<G[u].size();i++) { Edge e=edges[G[u][i]]; if(d[e.to]>d[e.from]+e.dist) { d[e.to]=d[e.from]+e.dist; path[e.to]=G[u][i]; Q.push(HeapNode(e.to,e.dist)); } } } } 打包成下面 struct Dijkskra { int n,m; vector<int> G[maxn]; //G[u][i] 表示u点连接的边的编号; vector<Edge> edges; //保存对应边的编号的一切信息 bool vis[maxn]; // int d[maxn],path[maxn]; void init(int n) { this->n=n; for(int i=0;i<n;i++) G[i].clear(); edges.clear(); } void AddEdge(int from,int to,int dist) { edges.push_back(from,to,dist); m=edges.size(); G[from].push_back(m-1); } void dijkstra(int s) { priority_queue<HeadNode> Q; Q.push(HeadNode(0,s)); for(int i=0;i<n;i++) d[i]=INF,vis[i]=false; d[s]=0; while(!Q.empty()) { HeapNode x=Q.top();Q.pop(); int u=x.u; if(vis[u]) continue; vis[u]=true; for(int i=0;i<G[u].size();i++) { Edge& e=edges[G[u][i]]; if(d[e.to]>d[e.from]+e.dist) { d[e.to]=d[e.from]+e.dist; path[e.to]=G[u][i]; Q.push(HeapNode(e.to,e.dist)); } } } } }
Bellman-Ford
对每个已更新过的节点都更新其相连的节点;
最短路最多经过n-1点(起点不算),经过m条边;
伪码描述
for(1~n-1) //最多检查n-1次
for(1~m) //每次每条边都要检查
{
if(x点已经更新) 更新周围的点;
}
因为Dijkstra是每次取出最小d[i],当存在负权就会出现问题;
而使用Bellman-Ford只是随意取出相邻边所以不会出现问题;
但正是因为是随意取出相邻边,所以对于每个节点要多次检查;
即 每个节点可以多次进入并检查该节点是否能更新 ,直到无法更新周围点;
对于起点i,其他的点都要扫一遍n-1,对于每一点相邻的边都要走一遍m
//简易版 O(m*n) for(int i=0;i<n;i++) d[i]=INF; d[0]=0; for(int k=0;k<n-1;k++) for(int i=0;i<m;i++) { int x=u[i],y=v[i]; if(d[x]<INF) d[y]=min(d[y],d[y]+w[i]); //if(x点更新过) //因为只有更新过才能更新你周围的点; } //优化版 队列优化 O(m*n) 但实际更快 bool inq[maxn]; //即inqueue 表示该点已经压入队列; int cnt[maxn]; //记录每个点收录次数,当出现cnt[i]>n 则说明不存在最短路径 ; bool Bellman-ford(int s) { queue<int> Q; memset(inq,0,sizeof(inq)); memset(cnt,0,sizeof(cnt)); for(int i=0;i<n;i++) d[i]=INF; d[s]=0; Q.push(s); while(!Q.empty()) { int u=Q.front();Q.pop(); inq[u]=false; //Q中取出来了,标为false; for(int i=0;i<G[u].size();i++) { Edge& e=edges(G[u][i]); if(d[u]<INF&&d[e.to]+e.dist) //若d[u]是INF表明u点未被更新,也就不用更新其周围的点; { d[e.to]=d[u]+e.dist; path[e.to]=G[u][i]; if(!inq[e.to]) { Q.push(e.to); inq[e.to]=true; //e.to点放入queue ,标识为true; if(++cnt[e.to]>n) return false; //图不通; } } } } return true; }
有三点要注意
1)每次for循环e.to只能压入队列一次,所以要用inq标识 ;
2)每个节点最多要放入队列n次(因为每个节点至少放入队列一次,若每个节点都可连通到x点,那么x点要进入队列n次)
所以cnt[e.to]>n 表明无最短路,可能存在负环;
3)每当e.to被更新过,就要放入队列更新e.to相连的点,直到所有的点都更新完,算法结束;
Floyd 多源最短路径 O(n^3)
通过一个图的权值矩阵求出它的每两点间的最短路径矩阵
i~j 要经过k点
d[0~n][0~n]=INF; d[0][0]=0; for(int k=0;k<n;k++) for(int i=0;i<n;i++) for(int j=0;j<n;j++) { if(d[i][j]<INF&&d[k][j]<INF) d[i][j]=min(d[i][j],d[i][k]+d[k][j]); }
相关文章推荐
- Source Insight 使用技巧
- Java初期阶段总结
- 添加操作列
- JSON 与 JSONP
- jsp的内置对象
- 渲染模板template_(内容管理系统总结3)
- cg toolkit中关于纹理投影的例子
- SQL数据库基础
- jsp的内置对象
- 获取文件(防锁占用)
- {sharepoint} More on SharePoint 2010 Application Pools
- {sharepoint}提升 SharePoint 代码执行权限
- 【Android】MTK Android 编译命令
- SQL常用练习
- AX 数据库日志如何选择正确的表名和字段名?
- Java读/写文件工具类
- jquery中的attr和prop深度理解
- 仿word导航窗口的展开与折叠
- excel中单列相乘取整
- Android_Page Curl:Android平台上的翻书翻页