今天写写最短路径的Floyd算法(有翻译叫弗洛伊德,不过这奇葩翻译用来读读就好……)。
这个算法的实质,广义来讲,其实是DP(动态规划)。其实按说,算法应该先说说什么贪心、搜索、DP、二分之类的基本算法的,但我觉得太广的东西对没有基础的人来说讲起来不清楚,还是先写写比较典型的一些算法比较好。而且这个系列是C++代码,并非过于详细的分析描述,那些以后有时间再写。
Floyd,求一个图中任意两点间的最短路径。图中有N个点,则算法复杂度是O(N³),空间复杂度是O(N²),编程复杂度极低。
先说下其中的基本操作,若i->j的当前路径不如i->k->j短,则更新最短路。这个操作叫做“松弛操作”,是最短路径算法中很基础的操作,就像排序中的“交换操作”一样基础,Floyd、Dijkstra、SPFA都是基于这个操作的。写成代码就是:
if(d[i][j]<d[i][k]+d[k][j]) d[i][j]=d[i][k]+d[k][j];
Floyd算法的思想是,以每个点分别为中间结点(上式中的k),去更新所有路径。例如对于u->v来说,最短路径是u->k1->k2->k3->k4->v,假设k1<k3<k2<k4,那么实际过程就是:
0、算法初始化,定义一个d数组,存的是初始任意两点间距离,若存在边u->v,则d[u][v] = u->v;若不存在边u->v,则d[u][v] = ∞(其实就相当于图的邻接矩阵,只不过没有的边补成了∞)
1、不妨设原本d[u][v] = ∞
2、在k=k1时,d[u][k2] = d[u][k1]+d[k1][k2] = u->k1->k2;
3、在k=k3时,d[k2][k4] = d[k2][k3]+d[k3][k4] = k2->k3->k4;
4、在k=k2时,d[u][k4] = d[u][k2]+d[k2][k4] = u->k1->k2->k3->k4;
5、在k=k4时,d[u][v] = d[u][k4]+d[k4][v] = u->k1->k2->k3->k4->v;
6、算法结束,d[u][v]中所存为最终最短路径,所有点对皆是如此,所以全部过程完结之后,任意两点的最短路径就都求出来了。
其实,这推导过程中,有些问题没有考虑到,因此算法的正确性在此文中并没有得到完整证明。时间所限,不再细说,有兴趣的同学可以想想,我有时间会再提的。
代码很简单,就三行:
for(int k=1;k<=N;++k) for(int i=1;i<=N;++i) for(int j=1;j<=N;++j) d[i][j] ← d[i][k]+d[k][j]; // 此处表示用右侧更新左侧,即松弛操作