Floyd (Floyd-Warshall) 弗洛伊德算法:
又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。
适用于多源最短路径,是一种动态规划的算法,稠密图效果更佳,边权可正可负。
优点:容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。
缺点:时间复杂度比较高(O(n3)),不适合计算大量数据。用在点比较少的起点不固定的问题中。能解决负边(负权)
但不能解决负环。
核心代码: (暴力枚举了所有的可能,将所有可能找遍就知道了两点之间的最短路)
n代表点的数量,m代表边的数量,w代表边的距离.
#define INF 99999999
void first()//初始化
{
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
dis[i][j]=0;//没有距离
}
else dis[i][j]=INF;//设为不可到达
}
}
}
更新边权
void updat(int x,int y,int w)
{
dis[x][y]=w;//单向边,如果为双向边,再加 dis[y][x]=w;
}
FLOYD:
找 i 到 j 的最短路:
1:直接从i 到 j
2: 从 i 通过若干个节点到达 j
dis[i][j] = min( dis[i][j],dis[i][k] + dis[k][j] )
void floyd(int x,int y)
{
for(int k=1;k<=n;k++){
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]);
}
}
}
printf("%d",dis[x][y]);
}
Dijkstra 迪杰斯特拉算法:
典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径(单源最短路)。是从一个顶点到其余
各顶点的最短路径算法, 要求图中不存在负权边。稀疏图(点的范围很大但边不多,需要更多地空间)。
解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
思路:
是一种贪心的策略,声明一个数组dis来保存起始源点到各个顶点的最短距离,通过不断的更新,找到各点到源点的
最短距离。
n,m,w;//n代表总共有多少点,m代表有多少条边,w代表边权
#define INF 9999999
初始化:
void first()
{
for(itn i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==j){
map[i][j]=0;
}
else map[i][j]=INF;
}
book[i]=0;
}
}
算法核心:
void Djikstra(int Source_point)//传入得数为源点
{
memset(dis,88,sizeof(dis));//把dis数组附最大值(88不是十进制的88,其实很大)
int Spt = Source_point;
book[Spt]=1;//标记源点已经被搜索过
for(int i=1;i<=n;i++){//进行第一遍更新源点到各点的最短距离
dis[i]=min(dis[i],map[Spt][i]);
}
for(int i=1;i<n;i++){
int minn=999999;
for(int j=1;j<=n;j++){
if(book[j]==0&&minn>dis[j]){
minn=dis[j];
Spt=j;//找到距离源点最近的值,存下来,用于下一次搜索·
}
}
book[Spt]=1;
for(int j=1;j<=n;j++){//将上次找到的距离源点最近的点作为源点,更新一遍
dis[j]=min(dis[Spt]+map[Spt][j],dis[j]);
}
}
}
Bellman-Ford算法:是求含负权图的单源最短路径的一种算法,效率较低,代码难度较小。
其原理为连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,
则说明图中有负环,因此无法得出结果,否则就完成。--baidu
用来解决单源最短路问题的。
初始化:
const int INF=9999999;
const int maxn=100005;
int front[maxn],next[maxn],w[maxn],dis[maxn];
int n,m;
void first()
{
for(int i=1;i<=n;i++){
dis[i]=INF;
}
dis[1]=0;
}
核心代码:
for(int k=1;k<n-1;k++){
int flag=0;
for(int i=1;i<=m;i++){
if(dis[next[i]] > dis[front[i]] + w[i]){
dis[next[i]] = dis[front[i]] + w[i];
flag=1;
}
}
if(flag==0){
break;
}
}
int flag1=0;
for(int i=0;i<m;i++){
if(dis[next[i]] > dis[front[i]] + w[i]){
flag1=1;
}
}
if(flag1==0){
cout<<"没有负权"<<endl;
}
else {
cout<<"you"<<endl;
}
SPFA算法:在 Bellman-ford 算法的基础上加上一个队列优化,减少了冗余的松弛操作,是一种高效的最短路算法。
适合稀疏图,可以解决有负权边问题,但在稠密图中效率不如Dijkstra。
设立一个先进先出的队列用来保存待优化的节点,优化时每次取出队首节点。
判断负环:如果某个点弹出的次数超过n-1次,则存在负环,对于存在负环的,无法计算单源最短路径。
const int maxn=1005;
int map[maxn][maxn],dis[maxn],inqueue_num[maxn];
int n,m,source,path[maxn];
bool book[maxn];
bool SPFA()
{
memset(book,0,sizeof(book));//标记顶点是否进入队列
memset(inqueue_num,0,sizeof(inqueue_num));//记录进入队列的次数
for(int i=0;i<n;i++){
dis[i]=INT_MAX;
path[i]=source;//记录最短路径
}
queue<int> Q;
Q.push(source);//将源点放进队列里
dis[source]=0;
book[source]=1;
inqueue_num[source]++;
while(!Q.empty()){
int u=Q.front();
Q.pop();
book[u]=0;
for(int v=0;v<n;v++){//用u点当前的最短路径值对离开u点所指的v点进行松弛,如果v点的
//最短路径值有所调整,且v点不在队列,将v点放入队列
if(map[u][v]!=INT_MAX){
if(dis[u]+map[u][v]<dis[v]){
dis[v]=dis[u]+map[u][v];
path[v]=u;
if(!book[v]){
Q.push(v);
inqueue_num[v]++;
if(inqueue_num[v]>=n){
return false;
}
book[v]=1;
}
}
}
}
}
return true;
}