Floyed(弗洛伊德)算法:
点u、v如果有边相连,则dis[u][v]=w[u][v]。w表示u-v点的距离
如果不相连则dis[u][v]=0x7fffffff,表示为无穷
For (k = 1; k <= n; k++)
For (i = 1; i <= n; i++)
For (j = 1; j <= n; j++)
If (dis[i][j] >dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
这是算法的核心代码,通过三重循环可以找到两点之间的最短距离,注意,是所有两个点之间的最短距离,但是时间复杂度有点高,是n的三次方。适合处理不确定起点,数据量较小的问题。
变形:
For (k = 1; k <= n; k++)
For (i = 1; i <= n; i++)
For (j = 1; j <= n; j++)
dis[i][j] = dis[i][j] || (dis[i][k] && dis[k][j]);
因为Floyed算法找到了所有的两点之间最短距离,故在没有边权的情况下,可以使边权是bool数,判断其是否相连。
dijkstra迪杰斯特拉算法:(从我以前的博客复制粘贴的,补充图论的最短路径的方法):
迪杰斯特拉算法不同于Floyed算法,迪杰斯特拉算法趋向于解决知道起点的问题,故不需要找到所有两点之间的最短距离,只需要找到起点距离终点的最短距离,故时间复杂度仅有n方。
迪杰斯特拉算法是一个最短路径的方法,在这里简称dij,它在确立出发点的基础上,设出发点是start,你想到达的点是end,其中一共有n个点,我们可以先找一步到达的距离,然后与分两步到达的距离比较,取最小值,然后让得到的结果与三步到达的距离比较,取最小值,最终让得的的最小值与n-1部到达的距离比较,取最小值,这样的最小值就是我们要得到的距离最少的最优点了。
但dij在c++中实现可不是那么好实现的,我们要把点之间的距离全部得到,在刚开始的几部中如果你要求start到end之间的距离,比如1,2,3点,1-2之间距离是1,2-3之间距离是1,1-3之间距离是1,这样1-3的距离最短就是1,为了避免类似1-2-3这样的情况,就是我上面一段说的那些了,我们要分步的比较,在c++中我们一般会用这样的方法:min是否大于1-3+3-4这样的形式,而1-3本身可能并不是最小值,因此我们在计算过程中不只把start到end的最小值算了出来,我们还把start到所有点的最小值算了出来。下面介绍一下dij在c++中实现的函数:
int i,j,k,min,tmp;
void dij(int start,int dist【】,flag【】) //dist【i】意是点start到点i的距离,flag【i】=0意思是点start到i的距离最小值未得到,=1意思是已经得到最短距离。我们确立flag的目的是防止重复与过多的循环。
{
for(i=1;i<=n;++i) //详细的没有指出,n是指全部的点
{flag[i]=0;
dist[i]=long[1][i] //long表示1-i之间的距离 }
flag【start】=1; //出发点与出发点的最短距离,默认为已知
dist【start】=0; //出发点与出发点的距离0;
//这之后就是在从start1步到达end和n-1步到达end的最短距离的比较选择,也是dij中的核心
for(i=1;i<n;++i){
min=inf; //inf是一个极大值,表示无穷大的数。
for(j=1;j<=n;++j) if(flag[j]==0&&dist[j]<min) {min=dist[j]; k=j;}
flag[k]=1; //因为一步里面到达的它是距离最少的,所以start到k点距离最小。
//下面的循环就是求第二步到第n-1步最小值的。不太好掌握
for(j=1;j<=n;++j){ tmp= long[k][j]==INF ? INF : (min + mMatrix[k][j])); if(flag[j]==0&&dist[j]>temp) dist【j】=tmp;}//不仅如此,在这一步中我们得到了每一个分两步到达各点的距离的最小值,然后返回上一个循环找到两步到达的最小值的点,然后再进行这一个循环得到分三步到达的点的距离的最小值,以此类推。
然后我们想要到哪个点,直接用dist【i】就是最小距离点了。
}
Bellman-Ford算法O(NE):
这是一种可以解决有负边权的算法,但是不能够解决负边权回路的问题(至于为什么之后再补)。同迪杰斯特拉算法相似,这个算法有起点,是找起点离所有点的最短距离。
dis[s]=0,dis[v]=∞(v≠s),pre[s]=0;//s是起始点无穷可以是0x7fffffff
实现原理:
For (i = 1; i <= n-1; i++)
For (j = 1; j <= E; j++) //枚举所有边,注意是边!!!
if (dis[u]+w[j]<dis[v]) //u、v是这条边连接的两个点。
{ dis[v] =dis[u] + w[j]; //求距离
pre[v] = u; } //pre在这里表示的是v点的前面走的是u
SPFA算法O(kE):
这个算法是Bellman-Ford算法的队列实现。
开始时把起点放入队列,之后从队列取出一个元素并把它相邻的点修改,若相邻的点修改成功则入队,直到队列为空。其形式上与广度优先搜索非常相似,但是其队列中取出的元素还可以返回,这是其一大特点。
算法实现:
dis[i]记录从起点s到i的最短路径,w[i][j]记录连接i,j的边的长度。pre[v]记录前趋。 team[1..n]为队列,头指针head,尾指针tail。
布尔数组exist[1..n]记录一个点是否现在存在在队列中。
初始化:dis[s]=0,dis[v]=∞(v≠s),//依然无穷可以是0x7fffffff
memset(exist,false,sizeof(exist));
team[1]=s; head=0; tail=1;exist[s]=true; //将第一个点入队
do {if (dis[v]>dis[u]+w[u][v]) //头指针要指向当前点的前驱方向,既u
{ dis[v]=dis[u]+w[u][v];
pre[v]=u;
if (!exist[v]) //如果未false,说明已经被取出
{//否则指针指向下一个,v入队
exist[v]=true; } } }
while (head < tail);