最短路径总结

目录

一、Dijkstra算法(单源无负权)

二、Floyd-Warshall算法(多源有负权)

三、SPFA算法(单源有负权)


一、Dijkstra算法(单源无负权)

分享:https://www.cnblogs.com/GnibChen/p/8875247.html

【问题】

又称迪杰斯特拉算法,是一个经典的最短路径算法,主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,使用了广度优先搜索解决赋权有向图的单源最短路径问题,算法最终得到一个最短路径树。时间复杂度为O(N^2)。

【基本步骤】

  • 将所有的顶点分为两部分:已知最短路程的顶点集合P和未知最短路径的顶点集合Q。最开始,已知最短路径的顶点集合P中只有源点一个顶点。我们这里用一个vis[ i ]数组来记录哪些点在集合P中。例如对于某个顶点i,如果vis[ i ]为1则表示这个顶点在集合P中,如果vis[ i ]为0则表示这个顶点在集合Q中。

  • 设置源点s到自己的最短路径为0即dis=0。同时把所有其它顶点的最短路径为设为∞。

  • 在集合Q的所有顶点中选择一个离源点s最近的顶点u(即dis[u]最小)加入到集合P。并考察所有以点u为起点的边,对每一条边进行松弛操作。例如存在一条从u到v的边,那么可以通过将边u->v添加到尾部来拓展一条从s到v的路径,这条路径的长度是dis[u]+e[u][v]。如果这个值比目前已知的dis[v]的值要小,我们可以用新值来替代当前dis[v]中的值。

  • 重复第3步,如果集合Q为空,算法结束。最终dis数组中的值就是源点到所有顶点的最短路径。

【代码】

const int inf=0x3f3f3f3f;
int cost[105][105];  //用于储存点与点之间的距离
void dijkstra(int a)  //求出点a到所有点的最短距离
{  
      int i,vis[105];  
      for(i=1;i<=n;i++)  //初始化dis[],vis[]  
      {  
           dis[i]=inf;  
           vis[i]=0;  
      }  
      dis[a]=0;  //显然点到自身距离为0
      while(1)  
      {  
           int v=-1;  
           for(i=1;i<=n;i++)  
                 if(!vis[i]&&(v==-1||dis[i]<dis[v]))  
                      v=i;  //修正当前最短路径的前驱结点
           if(v==-1) //所有路径均已标记完毕,退出循环  
                 break;  
           vis[v]=1;  //标记  
           for(i=1;i<=n;i++)  
                 dis[i]=min(dis[i],dis[v]+cost[v][i]);  //更新dis[]  
      }  
}  

【题目】

【ZCMU1398】工程

二、Floyd-Warshall算法(多源有负权)

分享:http://www.cnblogs.com/GnibChen/p/8890858.html

【问题】

通过邻接矩阵计算出所有点之间的最短路,时间复杂度O(n^3),空间复杂度O(n^2)  

【基本步骤】
弗洛伊德算法定义了两个二维矩阵:

  1. 矩阵D记录顶点间的最小路径 
    例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10;
  2. 矩阵P记录顶点间最小路径中的中转点 
    例如P[0][3]= 1 说明,0 到 3的最短路径轨迹为:0 -> 1 -> 3。

它通过3重循环,k为中转点,v为起点,w为终点,循环比较D[v][w] 和 D[v][k] + D[k][w] 最小值,如果D[v][k] + D[k][w] 为更小值,则把D[v][k] + D[k][w] 覆盖保存在D[v][w]中。

【代码】

int cost[105][105];  //用于储存点与点之间的距离
int P[105][105]; //记录顶点间最小路径中的中转点 
int D[105][105];//记录顶点间的最小路径 
void floyd()
{
	int i,j,k;
	for(i=0;i<n;i++){//初始化 
		for(j=0;j<n;j++){
			D[i][j]=cost[i][j];
			P[i][j]=j;
		}
	}
	for(k=0;k<n;k++){//遍历当每一个结点作为中转点 
		for(i=0;i<n;i++){
			for(j=0;j<n;j++){
				if(D[i][j]>D[i][k]+D[k][j]){
					D[i][j]=D[i][k]+D[k][j];
					P[i][j]=P[i][k];//更新顶点间最小路径的中转点 
				}
			}
		}
	}
}

 

三、SPFA算法(单源有负权)

【问题】

求解单源有负权问题,并且可以判断负环,期望的时间复杂度O(ke), 其中k为所有顶点进队的平均次数,可以证明k一般小于等于2。

SPFA的两种写法,bfs和dfs,bfs判别负环不稳定,所以可以用dfs判断负环,bfs计算最短路。

【基本步骤】

bfs:

  建立一个队列,初始时队列里只有起始点,再建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点作为起始点去刷新到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。

判断有无负环:
  如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图)

dfs:

       时间复杂度如果没记错是O(nlogn)的。

判断有无负环:

       否存在一点在一条路径上出现多次来判断。 

【代码】

bfs:


int spfa(int st,int ed)  
{  
    for(int i=1;i<=n;i++)  
    {  
        dis[i]=INF;  
        vis[i]=0;  
    }  
    dis[st]=0;  
    queue<int>q;  
    q.push(st);  
    vis[st]=1;  
    while(!q.empty())  
    {  
        int t=q.front();  
        q.pop();  
        vis[t]=0;  
        for(int i=1;i<=n;i++)  
        {  
            if(dis[i]>dis[t]+edge[t][i])  
            {  
                dis[i]=dis[nt]+edge[t][i];  
                if(vis[i]==0)  
                {  
                    q.push(i);  
                    vis[i]=1;  
                }  
            }  
        }  
    }  
    return dis[ed];  
}  

猜你喜欢

转载自blog.csdn.net/qq_39826163/article/details/82558539