Floyd Warshall 弗洛伊德算法---最短路径

3.4. 带权有向图中任意两点间的最短路径
1.问题的理解与描述
我们的问题是要找出图G中的每一个顶点到其他所有顶点的距离。此处,顶点i、j间的距离定义为从i出发到j的最短路径长度。这是一个组合优化问题,从i出发到j可能有若干条路径,每条路径都有其长度,目标是找到i到j的最短路径长度。问题形式化为:
输入:表示带权有向图 G=<V,E> 的n*n矩阵W。
输出:对任意的i,j∈V,i到j间的距离及最短路径。
2. 定理
G=<V, E>的两个不同的顶点i,j∈V={1, 2, …, n},p是从i到j其间仅经过{1, 2, …, k}的最短路径。
(1)若p不经过顶点k,则p也是从i到达j其间仅经过{1, 2, …, k-1}的最短路径。
(2)若p经过顶点k,即p:i>k>j。我们把前半段i > k记为p1,后半段k>j 记为p2。则p1是从i 到k其间仅经过{1, 2, …, k-1}的最短路径,p2是k到j其间仅经过{1, 2, …, k-1}的最短路径。
定义 dki,j 为i到j且其间仅经过{1, 2, …, k}的最短路径长度。根据上述的任意点对其间最短路径问题的最优子结构特性,有关于di,jk的递归式:

由此递归式的定义可知子问题具有重叠性。
记:

Dk=(dki,j)n×n,k=1,2,...,n

3. 算法的伪代码描述及其运行时间分析

FLOYD-WARSHALL(W)
1 D←W
2 for k←1 to n
3   do for i←1 to n
4       do for j←1 to n
5           if D[i, j]>D[i, k]+ D[k, j]
6               then D[i, j] ←D[i, k]+ D[k, j]
7 return D

显然,算法的时间复杂度为Θ(n3)。
4.构造一个最优解
为了构造最优解—— 任意顶点对(i, j)的最短路径,必须记录路径上的顶点序列。我们用跟踪路径上每一个顶点的前驱来达到这一目的: π(k)ij 定义为从顶点i到j的中间顶点均在集合{1, 2,…, k}中的最短路径上顶点j的前驱。则

那么 π(n)ij 就是顶点对(i,j)的最短路径中j 的前驱顶点
则可修改算法同时得到D,π矩阵

FLOYD-WARSHALL(W)
1 for i←1 to n
2   do for j←1 to n
3       do if i=j or W[i, j]=
4           then Π[i, j] ←NIL
5           else Π[i, j] ←i
6 D←W
7 for k←1 to n
8   do for i←1 to n
9       do for j←1 to n
10          if D[i, j]>D[i, k]+ D[k, j]
11              then D[i, j] ←D[i, k]+ D[k, j]
12                  Π[i, j] ←Π[k,j]
13 return D, Π

构造顶点对(i,j) 的最短路径

PRINT-ALL-PAIRS-SHORTEST-PATHS(Π, i, j)
1 if i=j
2   then print i
3   else if Π[i, j]=NIL
4      then print "no path from" i "to" j "exists"
5      else PRINT-ALL-PAIRS-SHORTEST-PATHS(Π,i, Π[i, j])
6              print j
  1. C++代码实现如下:
    FloydWarshall.cpp文件

#include "stdafx.h"
#include "FloydWarshall.h"

pair<vector<double>, vector<int>> floydWarshall(const vector<double> &w)
{

    int i, j, k, n = sqrt(w.size());
    vector<int>pi(w.size(), 0);//最短路径顶点j前驱
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            if (i == j || w.at(i*n + j) >= DBL_MAX)//i=j or w[i,j]=∞
                pi.at(i*n + j) = -1;//pi[i,j]=NIL
            else
                pi.at(i*n + j) = i; //pi[i,j]=i
    vector<double>D(w);//最短路径长度 D=W
    for (k = 0; k < n; k++)
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                if (D.at(i*n + j) > (D.at(i*n + k) + D.at(k*n + j)))//D[i,j]>D[i,k]+D[k,j]
                {
                    D.at(i*n + j) = D.at(i*n + k) + D.at(k*n + j);//D[i,j]=D[i,k]+D[k,j]
                    pi.at(i*n + j) = pi.at(k*n + j);//pi[i,j]=pi[k,j]
                }
    return make_pair(D, pi);

}
void printAllPairsShortestPaths(const vector<int>pi, int i, int j)
{
    int n = sqrt(pi.size());
    if (i == j)
    {
        cout << i + 1;
        return;
    }
    else if (pi.at(i*n + j) == -1)//pi[i,j]=NIL
        cout << "no path from" << i + 1 << "to" << j + 1 << "exists";
    else
    {
        printAllPairsShortestPaths(pi, i, pi.at(i*n + j));
        cout << j + 1;
    }  
}

FloydWarshall.h文件

#ifndef _FLOYDWARSHALL_H
#define _FLOYDWARSHALL_H

#include <cstring>
#include <cfloat>
#include <cstdio>
#include <utility>
#include <vector>
#include <iostream>
using namespace std;

pair<vector<double>, vector<int>> floydWarshall(const vector<double> &w);
void printAllPairsShortestPaths(const vector<int>pi, int i, int j);
#endif

测试main.cpp文件

// FloydWarshall.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "FloydWarshall.h"
//#include <utility>
//using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
    double a[] = { 0, 3, 8, DBL_MAX, -4,
        DBL_MAX, 0, DBL_MAX, 1, 7,
        DBL_MAX, 4, 0, DBL_MAX, DBL_MAX,
        2, DBL_MAX, -5, 0, DBL_MAX,
        DBL_MAX, DBL_MAX, DBL_MAX, 6, 0 };
    vector<double> w(a,a+25);
    int n = (int)sqrt(w.size());
    pair<vector<double>, vector<int>> r = floydWarshall(w);
    //print d
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            cout << r.first.at(i*n + j)<<" ";
        cout << endl;
    }
    //print pi
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
            cout << r.second.at(i*n + j) << " ";;
        cout << endl;
    }

    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n; j++)
        {
            printAllPairsShortestPaths(r.second, i, j);
            cout <<":"<< r.first.at(i*n + j)<<endl;
        }
    }
    system("pause");
    return 0;
}

运行结果:
0 1 -3 2 -4
3 0 -4 1 -1
7 4 0 5 3
2 -1 -5 0 -2
8 5 1 6 0
-1 2 3 4 0
3 -1 3 1 0
3 2 -1 1 0
3 2 3 -1 0
3 2 3 4 -1
1:0
15432:1
1543:-3
154:2
15:-4
241:3
2:0
243:-4
24:1
2415:-1
3241:7
32:4
3:0
324:5
32415:3
41:2
432:-1
43:-5
4:0
415:-2
541:8
5432:5
543:1
54:6
5:0
请按任意键继续…

参考:《算法设计,分析与实现:C、C++和Java》

你可能感兴趣的:(最短路径,warshall,floyd,弗洛伊德算法)