一.Floyd算法html
用于计算任意两个节点之间的最短路径。算法
参考了five20的博客数据结构
Floyd算法的基本思想以下:从任意节点A到任意节点B的最短路径不外乎2种可能,1是直接从A到B,2是从A通过若干个节点到B,因此,咱们假设dist(AB)为节点A到节点B的最短路径的距离,对于每个节点K,咱们检查dist(AK) + dist(KB) < dist(AB)是否成立,若是成立,证实从A到K再到B的路径比A直接到B的路径短,咱们便设置 dist(AB) = dist(AK) + dist(KB),这样一来,当咱们遍历完全部节点K,dist(AB)中记录的即是A到B的最短路径的距离。函数
标准五行代码以下:优化
for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(dis[i][k]+dis[k][j]<dis[i][j]) { dis[i][j]=dis[i][k]+dis[k][j]; } }
可是这里咱们要注意循环的嵌套顺序,若是把检查全部节点K放在最内层,那么结果将是不正确的,为何呢?由于这样便过早的把i到j的最短路径肯定下来了,而当后面存在更短的路径时,已经再也不会更新了。spa
更多关于Floyd算法,详细请见:Floyd算法百度百科连接.net
二.Dijkstra算法:code
适用于权值为非负的图的单源最短路径。htm
斐波那契堆优化的时间复杂度为O(E+VlogV),但其实只是理论值,实际中基本上达不到。
blog
算法思路:
参考了殷天文的博客
指定一个节点,例如咱们要计算 'A' 到其余节点的最短路径
引入两个集合(S、U),S集合包含已求出的最短路径的点(以及相应的最短长度),U集合包含未求出最短路径的点(以及A到该点的路径,注意 如上图所示,A->C
因为没有直接相连 初始时为∞)
初始化两个集合,S集合初始时 只有当前要计算的节点,A->A = 0
,
U集合初始时为 A->B = 4, A->C = ∞, A->D = 2, A->E = ∞
,敲黑板!!!接下来要的两个步骤是核心!
从U集合中找出路径最短的点,加入S集合,例如 A->D = 2
更新U集合路径,if ( 'D 到 B,C,E 的距离' + 'AD 距离' < 'A 到 B,C,E 的距离' )
则更新U
循环执行 四、5 两步骤,直至遍历结束,获得A 到其余节点的最短路径
朴素版时间复杂度O(n²)算法代码以下:
void Dijkstra() { memset(dis, 0x1f, sizeof(dis)); dis[1] = 0; for (int i=1; i<=n; i++) { int min_len = 1e9, k = 0; for (int j=1; j<=n; j++) if (!vis[j] && dis[j] < min_len) { min_len = dis[j]; k = j; } vis[k] = 1; for (int j=h[k]; j!=-1; j=edge[j].next) { int to = edge[j].to, w = edge[j].w; if (!vis[to] && dis[to] > dis[k]+w) { dis[to] = dis[k]+w; } } } }
稳定时间复杂度O(mlogn)的堆优化(优先队列代替)代码以下:
void Dijkstra_Heap() { priority_queue<pair<int, int> > q; memset(dis, 0x3f, sizeof(dis)); memset(vis,0,sizeof(v)); dis[1] = 0; q.push(make_pair(0, 1)); while (!q.empty()) { int now = q.top().second; q.pop(); if (vis[now]) continue; vis[now] = 1; for (int i=h[now]; i!=-1; i=edge[i].next) { int to = edge[i].to, w = edge[i].w; if (!vis[to] && dis[to]>dis[now]+w) { dis[to] = dis[now] + w; q.push(make_pair(-dis[to], to)); } } } }
这里有一个关于优先队列的小骚操做:
因为STL中的优先队列默认是大根堆,因此在使用push函数的时候只需在须要排序的数据前加个‘-’便可。
关于Dijkstra算法的选择上,对于稀疏图,因为n和m比较接近,故选择堆优化算法;而对于稠密图,因为点少边多,故选择朴素版算法。
更多关于Dijkstra算法,详细请见:Dijkstra算法百度百科连接
三.Bellman-Ford算法
参考博客:图解贝尔曼福特-算法
可用于解决如下问题:
算法思路:
思路上与狄克斯特拉算法(Dijkstra algorithm)最大的不一样是每次都是从源点s从新出发进行"松弛"更新操做,而Dijkstra则是从源点出发向外扩逐个处理相邻的节点,不会去重复处理节点,这边也能够看出Dijkstra效率相对更高点。
下面是有边数k限制的Bellman_ford算法模板:
void Bellman_ford() { memset(dis,0x1f,sizeof(dis)); dis[1]=0; for(int i=1;i<=k;i++) { memcpy(last,dis,sizeof(last)); for(int j=1;j<=m;j++) { int u=edge[j].u,v=edge[j].v,w=edge[j].w; if(dis[v]>last[u]+w)dis[v]=last[u]+w; } } }
更多关于Bellman-Ford算法,详细请见:Bellman-Ford算法百度百科连接
四.SPFA算法
SPFA是西安交通大学的段凡丁在1994年与《西安交通大学学报》中发表的“关于最短路径的SPFA快速算法”,他在里面说SPFA速度比Dijkstra快,且运行V次的SPFA速度比Floyd速度快。
而事实证实SPFA算法是有局限的,他不适用于稠密图,对于特别状况的稠密图,SPFA复杂度和BellmanFord时间同样。
适用范围:适用于权值有负值,且没有负圈的图的单源最短路径,论文中的复杂度O(kE),k为每一个节点进入Queue的次数,且k通常<=2,但此处的复杂度证实是有问题的,其实SPFA的最坏状况应该是O(VE).
注意:SPFA算法在网格图中很是慢
算法思路:
参考了小天位的博客
代码以下:
void SPFA() { memset(dis, 0x3f, sizeof(dis)); memset(vis, 0, sizeof(vis)); queue<int> q; dis[1] = 0; vis[1] = 1; q.push(1); while (!q.empty()) { int now = q.front(); q.pop(); vis[now] = 0; for (int i=h[now]; i!=-1; i=edge[i].next) { int to = edge[i].to, w = edge[i].w; if (dis[now]+w < dis[to]) { //在队列中的点也能够进行松弛操做,故这里不需加!ifq[to]的条件,而须要加在下面。 dis[to] = dis[now]+w; if (!vis[to]) q.push(to), vis[to] = 1; } } } }
更多关于SPFA算法,详细请见:SPFA算法百度百科连接
五.最小环问题
解决思路:
最小环就是指在一张图中找出一个环,使得这个环上的各条边的权值之和最小。在Floyed的同时,能够顺便算出最小环。
记两点间的最短路为dis[i][j],g[i][j]为边<i,j>的权值。
一个环中的最大结点为k(编号最大),与它相连的两个点为i,j,这个环的最短长度为g[i][k]+g[k][j]+(i到j的路径中,全部结点编号都小于k的最短路径长度)。
根据Floyed的原理,在最外层循环作了k-1次以后,dis[i][j]则表明了i到j的路径中,全部结点编号都小于k的最短路径。
综上所述,该算法必定能找到图中最小环。
代码以下:
参考了Coder_YX的博客
void floyd(){ int MinCost = inf; for(int k=1;k<=n;k++){ for(int i=1;i<k;i++) for(int j=i+1;j<k;j++) MinCost = min(MinCost,dis[i][j]+mp[i][k]+mp[k][j]);//更新k点以前枚举ij求通过ijk的最小环 for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]); //更新k点 } if(MinCost==inf)puts("It's impossible."); else printf("%d\n",MinCost); }
关于四种最短路算法的结论:
参考了xiazdong的博客
(1)当权值为非负时,用Dijkstra。
(2)当权值有负值,且没有负圈,则用SPFA,SPFA能检测负圈,可是不能输出负圈。
(3)当权值有负值,并且可能存在负圈,则用BellmanFord,可以检测并输出负圈。
(4)SPFA检测负环:当存在一个点入队大于等于V次,则有负环。