目录
一、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[]
}
}
【题目】
二、Floyd-Warshall算法(多源有负权)
分享:http://www.cnblogs.com/GnibChen/p/8890858.html
【问题】
通过邻接矩阵计算出所有点之间的最短路,时间复杂度O(n^3),空间复杂度O(n^2)
【基本步骤】
弗洛伊德算法定义了两个二维矩阵:
- 矩阵D记录顶点间的最小路径
例如D[0][3]= 10,说明顶点0 到 3 的最短路径为10; - 矩阵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];
}