最短路径算法(下)——弗洛伊德(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 举报

相关文章推荐

html 页面之间跳转和传值

从a.html 跳转到b.html 并吧 a.html 的值传入b.html a.html New Document function to (){...

jsp页面间的传值方法

JSP页面间传递参数是经常需要使用到的功能,有时还需要多个JSP页面间传递参数。下面介绍一下实现的方法。(1)直接在URL请求后添加如:直接传递参数特别的在使用response.sendRedirec...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

JS页面间传值

一:JavaScript静态页面值传递之URL篇 能过URL进行传值.把要传递的信息接在URL上. 例子: 参数传出页面Post.htm—>   function Post(...

安卓实现扫一扫识别数字

公司业务需求,需要做手机号码的识别。所以有了此篇文章,现在就将实现过程分享给大家。

前端在html页面之间传递参数的方法

项目中经常会出现的一种情况,有一个列表,譬如是案例列表,点击列表中的某一项,跳转至详情页面。详情是根据所点击的某条记录生成的,因为案例和具体的详情页面,都是用户后期自行添加的,我们开始编写时,不可能穷...

大四课程设计之基于RFID技术的考勤管理系统(一)项目介绍

大四课程设计之基于RFID技术的考勤管理系统(一)项目介绍

Spring Cloud在国内中小型公司能用起来吗?

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题Spring Cloud在国内中小型公司能用起来吗?,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将...

HTML页面之间跳转传值

1.借助JQuery,通过URL拼接,从而传递数据。   jquery.params.js用于两个HTML网页之间的传值。a.html?name=waley&age=20;b.html页面则可以这样获...

Ajax 和 JavaScript 验证用户登录

Ajax 和 JavaScript 验证用户登录 index.html 用户登录 .text { width:180px; height:21px; } .userRed...

java开启多个线程,执行完成后再执行主线程

博主昨天去一家公司面试,被面试官问到一个问题,如果开启10个线程,但是需要这10个线程都执行完成后,再交由主线程统一输入,如何实现?当时我没有回答,我对多线程并不是那么的熟悉,面试结束后,我通过查阅资...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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