SPFA解决单源最短路径
SPFA(Shortest Path Faster Algorithm):
一:基本算法
在求解单源最短路径的时候,最经典的是 Dijkstra 算法,但是这个算法对于含有负权的图就无能为力了,而 Bellman - Ford 算法的复杂度又过于高,这时 SPFA就应运而生了. SPFA 在 Bellman - Ford 算法的基础上进行了改进,使其能够在计算带有负权的图的单源最短路径的基础上,时间复杂度大幅降低.
众所周知 Bellman -Ford 算法会对每条边进行 n - 1 次检查,但是在这些检查过程中,有许多检查是没有必要的.事实上,唯一应该检查的边存在一个特点:这些边的起点在上一次处理时到源点的距离发生了变化,即松弛成功的边的终点.既然如此,那么就可以对算法进行优化,即每遍只处理特殊的点,这些点是上一次在松弛过程中某条边的终点.设立一个先进先出的队列来维护这些待处理的点,优化时每次取出队首节点 u, 并且对所有从点 u 出发的边进行松弛操作,对于每条边的终点 v,如果以点 v 为终点的边松弛成功, 且 v 不在队列内, 就将点 v 加入队尾.这样不断从对列取出节点来进行松弛操作,直至队列空为止.这个算法保证只要最短路径存在, SPFA 必定能求出最小值.
SPFA同样可以判断负环,如果某个点弹出队列的次数超过 N - 1 次,则存在负环.对于存在负环的图,是无法计算出单源最短路径的.
二:伪代码
在以下说明中, inque[] 记录点是否在队列中, 使用链式前向星存图, 结果保留在 dist[] 中.
(1):初始化,源的距离 dist[s] 设为 0, 其他点的距离设置为 INF, 新建一个队列, 将源点 s 入队, 标记源点 s 已经在对列中.
(2):从队首取出一个点 top, 标记top出队, top 出队的次数加 1,并对这个次数检查, 如果大于 n, 说明出现负环, 结束算法. 否则遍历从点 top 出发的边, 如果边 k 的终点 to 的 dist[] 可以更新, 即 dist[ edge[k].to ] > dist[top] + edge[k].w, 则更新 dist[ edge[k].to ] = dist[top] + edge[k].w, 检查 to 是否在队列内, 如果不在则加入队列.
(3)重复执行步骤(2), 直至队列为空.
三:以图为列
初始化队列、图和 dist[] 数组:
1.选择队列首部的点 “5”,对从此点出发的边 <"5", "3"> 和 <"5", "4"> 进行松弛:
2.选择队列首部的点 “3”,对从此点出发的边 <"3", “4”> 进行松弛, 松弛失败, 点 ”4“ 不会再次加入队列:
3.选择队列首部的点 “4”,对从此点出发的边 <"4", "1"> 和 <"4", "6">进行松弛:
4.选择队列首部的点 “1”,对从此点出发的边 <"1", '2"> 和 <"1", "6">进行松弛, 这里点”6“已经在队列内了,所以点 “6” 不会加入队列.
5.选择队列首部的点 “6”,对从此点出发的边<"6", "2"> 和 <"6", "5"> 进行松弛,.对边 <"6", "5"> 松弛失败, 虽然点 “5” 不在队列内, 但也不应该加入队列.
6.选择队列首部的点 “2”,对从此点出发的边<"2", "3"> 和 <"2", "5"> 进行松弛,.对边 <"2", "5"> 松弛失败, 点 “5” 不应该加入队列.
7.选择队列首部的点 “3”,对从此点出发的边<"3", "4"> 进行松弛,.且对边 <"3", "4"> 松弛失败, 此时队列为空算法结束.
四:代码
const int MAXN = ;
const int MAXE = ;
const int INF = INT_MAX;
int dist[MAXN + ];// dist[i] 表示点 i 到源点 s 的最短距离 int head[MAXN + ];//链式前向星存图
struct NODE { int to; int w; int next; };
NODE edge[MAXE]; bool SPFA(int n, int s) {//s 为源点 n 为总的点数
for(int i = ; i <= n; i++) dist[i] = INF;//初始化
dist[s] = ;
queue<int> rex;//保存待优化的节点
rex.push(s);//从源点 s 开始优化s
bool inque[MAXN + ] = { false };//inque[i] = false 表示点 i 在队列内
inque[s] = true;
int outque[MAXN + ] = { };//记录每个点出对列的次数
while(!rex.empty()) {
int top = rex.front();//取队首的点为待拓展节点
rex.pop();
inque[top] = false;
outque[top] ++;
if(outque[top] > n) return false;//存在负环
for(int k = head[top]; k != -; k = edge[k].next) {//对从当前点出发的所有边进行松弛操作
if(dist[top] != INF && dist[ edge[k].to ] > dist[top] + edge[k].w) {
dist[ edge[k].to ] = dist[top] + edge[k].w;
if(!inque[ edge[k].to ]) {//如果当前边另外一端的顶点不在队列内,则加入到队尾
rex.push( edge[k].to );
inque[ edge[k].to ] = true;
}
}
}
}
return true;
}
参考书籍 <<图论及应用>> 和网上部分资料.
SPFA解决单源最短路径的更多相关文章
- SPFA求单源最短路径
序 求最短路径的算法有很多,各有优劣. 比如Dijkstra(及其堆(STL-priority_queue)优化),但是无法处理负环的情况: 比如O(n^3)的Floyd算法:比如Bellman-Fo ...
- Dijkstra算法解决单源最短路径
单源最短路径问题:给定一个带权有向图 G = (V, E), 其中每条边的权是一个实数.另外,还给定 V 中的一个顶点,称为源.现在要计算从源到其他所有各顶点的最短路径长度.这里的长度是指路上各边权之 ...
- Poj 1860 Currency Exchange(Bellman-Ford,SPFA解单源最短路径问题)
一.题意 有多个货币交易点,每个只能互换两种货币,兑换的汇率不同,并收取相应的手续费.有N种货币,假定你拥有第S中,数量为V,有M个兑换点.问你能不能通过兑换操作使你最后拥有的S币比起始的时候多. 二 ...
- Dijkstra 单源最短路径算法
Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...
- Bellman-Ford 单源最短路径算法
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法.该算法由 Richard Bellman 和 Leste ...
- 单源最短路径-迪杰斯特拉算法(Dijkstra's algorithm)
Dijkstra's algorithm 迪杰斯特拉算法是目前已知的解决单源最短路径问题的最快算法. 单源(single source)最短路径,就是从一个源点出发,考察它到任意顶点所经过的边的权重之 ...
- matlab练习程序(单源最短路径Dijkstra)
图的相关算法也算是自己的一个软肋了,当年没选修图论也是一大遗憾. 图像处理中,也有使用图论算法作为基础的相关算法,比如图割,这个算法就需要求最大流.最小割.所以熟悉一下图论算法对于图像处理还是很有帮助 ...
- 【算法】单源最短路径和任意两点最短路径总结(补增:SPFA)
[Bellman-Ford算法] [算法]Bellman-Ford算法(单源最短路径问题)(判断负圈) 结构: #define MAX_V 10000 #define MAX_E 50000 int ...
- 基于visual Studio2013解决算法导论之043单源最短路径dijstra矩阵
题目 单源最短路径dijstra矩阵 解决代码及点评 // 26单源最短路径dijstra矩阵.cpp : 定义控制台应用程序的入口点. // #include <iostream> ...
随机推荐
- BZOJ4373 算术天才⑨与等差数列(线段树)
看上去很难维护,考虑找一些必要条件.首先显然最大值-最小值=k*(r-l).然后区间内的数需要模k同余.最后区间内的数两两不同(k=0除外).冷静一下可以发现这些条件组合起来就是充分的了. 考虑怎么维 ...
- TCP/IP Note2
TCP/IP寻址 TCP/IP使用32个比特或者4个0到255之间的数字来为计算机编址. 1. IP地址 每个计算机必须有一个IP地址才能够连入Internet中. 每个IP包必须有一个地址才能够发送 ...
- Thymeleaf 如何支持java8的时间LocalDate和LocalDatetime
一.添加依赖 <dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thym ...
- [AT3857]Median Sum
题目大意:给定$n$个数,第$i$个数为$a_i$,记这$n$个数的所有非空子集的和分别为$s_1,s_2,\dots,s_{2^n-1}$:求$s$的中位数. 题解:假设考虑的是所有子集,包括空子集 ...
- JS获取当前时间及时间戳相互转换
1.获取当前时间的 时间戳 Date.parse(new Date()) 结果:1486347562000 2.获取当前 时间 new Date() 结果:Mon Feb 06 2017 10:19: ...
- springMvc4+hibernate4+activiti5.15(Maven)
首先创建activiti表 方式1.用代码创建 package createtable; import org.activiti.engine.ProcessEngineConfiguration; ...
- 动态规划:LCS
先上状态转移方程,还是很容易看明白的 例题是Codevs的1862,这个题不是实现了方程就可以了的,还要完成一个事情那就是计数,数一数到底有多少个最长公共子序列 #include<cstdio& ...
- kettle基础操作
ETL:抽取(extract).转换(transform).加载(load)至目的端的过程: Kettle是ETL工具代表之一,是pentaho中的一个数据整合的一个组件.Kettle里包括多个Job ...
- JetbrainsCrack
http://blog.csdn.net/lcyong_/article/details/61205672 http://blog.csdn.net/nn_jbrs/article/details/7 ...
- 【洛谷 P4342】[IOI1998]Polygon(DP)
题目链接 题意不再赘述. 这题和合并石子很类似,但是多了个乘法,而乘法是不满足"大大得大"的,因为两个非常小的负数乘起来也会很大,一个负数乘一个很大的整数会很小,所以我们需要添加一 ...