最短路径算法(下)——弗洛伊德(Floyd)算法

原创 2017年09月13日 13:48:34

概述

在这篇博客中我主要讲解最短路径算法中的Floyd算法,这是针对多源最短路径的一个经典算法。对于单源最短路径算法请详见我的另一篇博客:最短路径算法(上)——迪杰斯特拉(Dijikstra)算法

弗洛伊德(Floyd)算法是解决任意两点间的最短路径的一种算法,可以正确处理有向图或有向图或负权(但不可存在负权回路)的最短路径问题,同时也被用于计算有向图的传递闭包。

算法思想与过程

(一)算法思想:
Floyd算法是一个经典的动态规划算法。用通俗的语言来描述的话,首先我们的目标是寻找从点i到点j的最短路径。从动态规划的角度看问题,我们需要为这个目标重新做一个诠释(这个诠释正是动态规划最富创造力的精华所在)。

从任意节点i到任意节点j的最短路径不外乎2种可能,一是直接从i到j,二是从i经过若干个节点k到j。所以,我们假设Dis(i,j)为节点u到节点v的最短路径的距离,对于每一个节点k,我们检查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,证明从i到k再到j的路径比i直接到j的路径短,我们便设置Dis(i,j) = Dis(i,k) + Dis(k,j),这样一来,当我们遍历完所有节点k,Dis(i,j)中记录的便是i到j的最短路径的距离。

(二)算法过程
1)首先把初始化距离dist数组为图的邻接矩阵,路径数组path初始化为-1。其中对于邻接矩阵中的数首先初始化为正无穷,如果两个顶点存在边则初始化为权重   
2)对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是就更新它。
状态转移方程为
如果 dist[i][k]+dist[k][j] < dist[i][j]
则dist[i][j] = dist[i][k]+dist[k][j]

//Floyd算法(多源最短路径算法) 
bool Floyd(){
    for(int k = 1 ; k < this->Nv+1 ; k++){  //k代表中间顶点 
        for(int i = 1  ; i < this->Nv+1 ; i++){//i代表起始顶点 
            for(int j = 1 ; j < this->Nv+1 ; j++){//j代表终点 
                if(this->dist[i][k] + this->dist[k][j] < this->dist[i][j]){
                    this->dist[i][j] = this->dist[i][k] + this->dist[k][j];
                    if(i == j && this->dist[i][j] < 0){//发现了负值圈 
                        return false;
                    }
                    this->path[i][j] = k;
                }                   
            }
        }
    }
    return true; 
}

例子

我们用如下图结构来演示Floyd算法:
这里写图片描述
全部代码为:

#include <iostream>
#include <cstring>
#include <stack>
#include <queue>
using namespace std;

const int MAX = 65535;

class Graph{
    private:
        int** G;        //邻接矩阵
        int** dist;     //距离数组 
        int** path;     //路径数组 
        int Nv;         //顶点数
    public:
        //构造函数
        Graph(int nv, int ne){
            this->Nv = nv;
            G = new int*[nv+1];
            dist = new int*[nv+1];
            path = new int*[nv+1]; 
            for(int i = 0 ; i < nv+1 ; i++){
                G[i] = new int[nv+1];
                dist[i] = new int[nv+1];
                path[i] = new int[nv+1];
                memset(path[i],-1,sizeof(path[0][0])*(nv+1));
                for(int j = 0 ; j < nv+1 ; j++){
                    this->G[i][j] = this->dist[i][j] = MAX;
                } 
            }
            cout<<"请输入边与权重:"<<endl;
            for(int i = 0 ; i < ne ; i++){
                int v1,v2,weight;
                cin>>v1>>v2>>weight;
                this->G[v1][v2] = this->G[v2][v1] = weight;
                this->dist[v1][v2] = this->dist[v2][v1] = weight;
            }   
        }

        //Floyd算法(多源最短路径算法) 
        bool Floyd(){
            for(int k = 1 ; k < this->Nv+1 ; k++){  //k代表中间顶点 
                for(int i = 1  ; i < this->Nv+1 ; i++){//i代表起始顶点 
                    for(int j = 1 ; j < this->Nv+1 ; j++){//j代表终点 
                        if(this->dist[i][k] + this->dist[k][j] < this->dist[i][j]){
                            this->dist[i][j] = this->dist[i][k] + this->dist[k][j];
                            if(i == j && this->dist[i][j] < 0){//发现了负值圈 
                                return false;
                            }
                            this->path[i][j] = k;
                        }                   
                    }
                }
            }
            return true; 
        }

        //打印start顶点到end顶点的路径 
        void Print_Path(int start,int end){
            stack<int> stack;
            queue<int> queue;
            int k = this->path[start][end]; //start与end之间的路径必须经过的顶点 
            int tmp = k;        //临时保存k
            if(k == -1){    //如果start与end之间没有中间结点
                //打印start与end后结束函数 
                cout<<start<<"->"<<end<<endl;
                return;
            } 
            stack.push(k);      //中间顶点入栈 
            //首先找到start与k之间的路径
            //由于是从后往前找路径,故利用堆栈 
            while(this->path[start][k] != -1){
                stack.push(this->path[start][k]);
                k = this->path[start][k]; 
            }
            stack.push(start);
            //然后找到k与end之间的路径
            //由于是从前往后找路径,故用队列 
            while(this->path[tmp][end] != -1){
                queue.push(this->path[tmp][end]);
                tmp = this->path[tmp][end]; 
            }
            queue.push(end);
            //打印路径 
            cout<<stack.top();
            stack.pop();    
            while(!stack.empty()){
                cout<<"->"<<stack.top();
                stack.pop();
            }
            while(!queue.empty()){
                cout<<"->"<<queue.front();
                queue.pop();
            }
            cout<<endl;
        }

        void Print_Floyd(){
            int i,j,k;
            cout<<" length      path"<<endl; 
            for(i = 1 ; i < this->Nv+1 ; i++){
                for(j = i+1 ; j < this->Nv+1 ; j++){
                    cout<<i<<"->"<<j<<" ";
                    cout<<this->dist[i][j]<<"       ";  
                    this->Print_Path(i,j);
                }
                cout<<endl;
            }
        }
};

int main()
{
    cout<<"请输入顶点数与边长数:"<<endl;
    int nv,ne;
    cin>>nv>>ne; 
    Graph graph(nv,ne);
    if(graph.Floyd()){
        cout<<"各个顶点的最短路径为:"<<endl; 
        graph.Print_Floyd();
    }

    return 0;
 }  

截图如下:
这里写图片描述

版权声明:本文为博主原创文章,若需转载,请注明http://blog.csdn.net/qq_30091945

相关文章推荐

最短路径算法(3)—Floyd(弗洛伊德)算法

转载来源:http://www.wutianqi.com/?p=1903

图搜索之基于Python的迪杰斯特拉算法和弗洛伊德算法

图搜索之基于Python的迪杰斯特拉算法和弗洛伊德算法Djstela算法#encoding=UTF-8 MAX=9 ''' Created on 2016年9月28日 @author: sx ''' ...

最短路径算法(上)——迪杰斯特拉(Dijikstra)算法

概述单源最短路径问题,即在图中求出给定顶点到其它任一顶点的最短路径。在弄清楚如何求算单源最短路径问题之前,必须弄清楚最短路径的最优子结构性质。最短路径的最优子结构性质描述为:如果P(i,j)={Vi…...

最小生成树算法(上)——Prim(普里姆)算法

概述最小生成树:一个有 n 个结点的连通图的生成树是原图的极小连通子图,且包含原图中的所有 n 个结点,并且有保持图连通的最少的边。根据定义可知对于一个有V个顶点的图来说,其最小生成树定包含V个顶点与...

数据结构看书笔记(十)—— 求最短路径问题之--迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd)算法

最短路径: 对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。 对于非网图来说,完全可以理解为所有边的权值都为1的网。 ...

弗洛伊德最短路径算法

下面是我按照严蔚敏老师的C语言版数据结构P189编写的弗洛伊德算法,在VC++6.0下运行通过#include "stdio.h" #include "stdlib.h" #define MAX ...

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

最短路径--弗洛伊德(Floyd)算法       最短路径问题。即寻找图中某两个特定结点间最短的路径长度。所谓图上的路径,即从图中一个起始结点到一个终止结点途中经过的所有结点序列,路径的长度...

最短路径之弗洛伊德算法(Floyd)

一道华为的机试题来展现弗洛伊德最短路径算法。 弗洛伊德算法优点:代码量少直观易理解。...

弗洛伊德(Floyd)算法求图的最短路径

弗洛伊德基本思想弗洛伊德算法作为求最短路径的经典算法,其算法实现相比迪杰斯特拉等算法是非常优雅的,可读性和理解都非常好。 基本思想: 弗洛伊德算法定义了两个二维矩阵: 矩阵D...
  • jeffleo
  • jeffleo
  • 2016年11月26日 11:44
  • 561
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:最短路径算法(下)——弗洛伊德(Floyd)算法
举报原因:
原因补充:

(最多只允许输入30个字)