图论算法之Floyd算法【寻找有向图中任意两点间的最短路径 C#实现】

图论算法之Floyd算法【寻找有向图中任意两点间的最短路径 C#实现】

Folyd 算法被称为图论算法中最优美的算法,其核心代码只有 5 行,相比与 Dijkstra 算法,获取有向图中任意两点之间的距离的时间复杂度一样,都为O(N^3),其思想类似于贪心算法,有强有力的数学理论:

Dis[a,b] = Dis[a,c] + Dis[c,b] < Dis[a,b] ? Dis[a,c] + Dis[c,b] : Dis[a,b]

上面这个表达式表示,一旦发现 a–>c–>b 距离比 a–>b 的距离更短后,就更新 Dis[a,b] 的距离为目前已知的最短距离。

Path[a,b] = Dis[a,c] + Dis[c,b] < Dis[a,b] ? Path[c,b] : Path[a,b]

上面这个表达式的意思是: 虽然我不知道 c 怎么到达 b,但是根据目前的信息我知道a 通过 c 再到达 b 的距离更短,那么 a 当然选择先到达 c ,到达 c 之后想办法怎么从 c 到达 b

其中Path[c,b] 中存储的是一个节点,这个节点是 c–>b 的完整路径上的最后一个与 b 相连的节点。
那么当 a–>b 的路径方案改为 a–>c–>b 后,显而易见这个完整的路径的最后一个与 b 相连的节点就是 Path[c,b] 中存储的节点。

这样通过 Path[,] 可以存储所有完整路径的与终点连接的上一个节点,这样就将所有点的关系紧密联系在了一起,虽然我不知道怎么去一个点,但是我可以问他,类似于下面这样:

A 想知道怎么到达 F,但是他不知道怎么去他就这样做:
A 先去问 F : 我怎么到 F 最短 ; F 说:完整的路径我记不住,我只记得你通过 E 到我这最短;
A又去问 E :我怎么到 E 最短 ;E 说:完整的路径我记不住,我只记得你通过 C 到我这最短;
A 先去问 C : 我怎么到 C 最短 ; C 说:完整的路径我记不住,我只记得你通过 D 到我这最短;
A又去问 D :我怎么到 D 最短 ;D 说:完整的路径我记不住,我只记得你通过 B 到我这最短;
A又去问 B :我怎么到 B 最短 ;B 说:你直接过来就是最短的;

当然,由于语言水平有限,如果没明白我在说什么也没关系,直接看代码:

核心代码:

        public void FloydGetPath(int NodeNum, double[,] Amat)
                {
                    FloydInit(NodeNum,Amat);               
                    int k, i, j;
                    for (k = 0; k < NodeNum; k++) //对每个节点尝试是否可以作为到达其它点的中间节点
                        for (i = 0; i < NodeNum; i++)
                            for (j = 0; j < NodeNum; j++)
                                if (AdjacentMat[i, k] + AdjacentMat[k, j] < AdjacentMat[i, j])
                                {
                                    AdjacentMat[i, j] = AdjacentMat[i, k] + AdjacentMat[k, j];//Adjacent 会不断更新 最后得到最短路径
                                    Path[i, j] = Path[k, j]; //更新 i-->j 的前驱节点  
                                    //假设Path[k, j]是点 M ;
                                    //则 M 是 k --> j 完整通路的 j 的前一个节点
                                    //当 i --> j 改为通过 k 到达 j 后,那么这条完整通路的就是 i-->j-->k
                                    //明显完整通路的 j 的前一个节点也一定是 M
                                }
                    ShowPath();
                    ShowAdjacentMat();
                    ShowSinglePath(0, 6);
                    ShowSinglePath(1, 5);
                }

完整工程代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Floyd算法
{
    class Program
    {
        static void Main(string[] args)
        {
            int NodeNumber = 7;//定义节点数目
            string ExcelPath = @"test.xlsx";
            double[,] pathdata = OpenExcel.GetDataFormExcel(ExcelPath);
            ShowData(pathdata);
            Floyd floyd = new Floyd();
            floyd.FloydGetPath(NodeNumber, pathdata);//第一个参数为节点数目 第二个参数为输入的 N 行 4 列的数据
            // 是一个N行4列的二维数组 
            // 第0列是路段编号【不必须有】 
            // 第1列是起点 
            // 第2列是终点
            // 第3列是有向距离
            Console.Read();            
        }

        

        #region  显示输入的数据
        static void ShowData(double[,] pathdata)
        {
            int a = 0;
            Console.WriteLine("输入的数据为:".PadRight(20));
            foreach (double item in pathdata)
            {
                a++;
                MyConsole.Write(item.ToString(), 10, ' ');
                if (a == 4)
                {
                    Console.WriteLine();
                    a = 0;
                }
            }
            Console.WriteLine();

        }
        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Floyd算法
{
    class Floyd
    {
        int NodeNumber;//节点数目
        double[,] AdjacentMat;//邻接矩阵
        int[,] Path;//路径

        #region 初始前驱节点
        void FloydInit(int NodeNum,double[,] Amat)
        {
            GetAdjacentMat(Amat,NodeNum);
            Path = new int[NodeNum, NodeNum];
            for (int i = 0; i < NodeNum; i++)
                for (int j = 0; j < NodeNum; j++)
                {
                    if (AdjacentMat[i, j] != double.PositiveInfinity)
                        Path[i, j] = i;
                    else
                        Path[i, j] = -1 ;
                }                    
        }
        #endregion

        #region Floyd主函数

        public void FloydGetPath(int NodeNum, double[,] Amat)
                {
                    FloydInit(NodeNum,Amat);               
                    int k, i, j;
                    for (k = 0; k < NodeNum; k++) //对每个节点尝试是否可以作为到达其它点的中间节点
                        for (i = 0; i < NodeNum; i++)
                            for (j = 0; j < NodeNum; j++)
                                if (AdjacentMat[i, k] + AdjacentMat[k, j] < AdjacentMat[i, j])
                                {
                                    AdjacentMat[i, j] = AdjacentMat[i, k] + AdjacentMat[k, j];//Adjacent 会不断更新 最后得到最短路径
                                    Path[i, j] = Path[k, j]; //更新 i-->j 的前驱节点  
                                    //假设Path[k, j]是点 M ;
                                    //则 M 是 k --> j 完整通路的 j 的前一个节点
                                    //当 i --> j 改为通过 k 到达 j 后,那么这条完整通路的就是 i-->j-->k
                                    //明显完整通路的 j 的前一个节点也一定是 M
                                }
                    ShowPath();
                    ShowAdjacentMat();
                    ShowSinglePath(0, 6);
                    ShowSinglePath(1, 5);
                }
                #endregion

        #region 获取邻接矩阵
                /// <summary>
                /// 为邻接矩阵赋值
                /// </summary>
                /// <param name="Amat">
                /// 是一个N行4列的二维数组 
                /// 第0列是路段编号【不必须有】 
                /// 第1列是起点 
                /// 第2列是终点
                /// 第3列是有向距离
                /// </param>
                /// <param name="NodeNum"></param>
                public void GetAdjacentMat(double[,] Amat, int NodeNum)
                {
                    NodeNumber = NodeNum;
                    AdjacentMat = new double[NodeNumber, NodeNumber];
                    for (int i = 0; i < NodeNum; i++)     //不连通的距离为无穷大
                        for (int j = 0; j < NodeNum; j++)
                            {
                                if (i == j)
                                    AdjacentMat[i, j] = 0;
                                else
                                    AdjacentMat[i, j] = double.PositiveInfinity;
                            }

                    for (int row = 0; row < Amat.GetLength(0); row++)
                    {
                        AdjacentMat[(int)Amat[row, 1], (int)Amat[row, 2]] = Amat[row, 3];
                    }
                    ShowAdjacentMat();
                }
                #endregion

        #region 显示
        private void ShowAdjacentMat()
                {
                    int num = 0;
                    Console.WriteLine("AdjacentMat邻接矩阵:\n".PadRight(20));
                    foreach (double item in AdjacentMat)
                    {
                        MyConsole.Write(item.ToString(), 10, ' ');
                        num++;
                        if (num == NodeNumber)
                        {
                            num = 0;
                            Console.WriteLine();
                        }  
                    }
                    Console.WriteLine();
                }

        private void ShowPath()
        {
            int num = 0;
            Console.WriteLine("Path:\n");
            foreach (int item in Path)
            {
                if (num == NodeNumber)
                {
                    Console.WriteLine();
                    num = 0;
                }
                MyConsole.Write(item.ToString(), 10, ' ');
                num++;
            }
            Console.WriteLine("\n\n");     
        }

        private void ShowSinglePath(int startNode, int endNode)
        {
            Console.WriteLine("从" + startNode.ToString() + "到" + endNode.ToString() + "最短路径长度为:" + AdjacentMat[startNode, endNode].ToString() );
            Console.Write("从" + startNode.ToString() + "到" + endNode.ToString() + "最短路径为:");            
            List<string> singlepath = new List<string>();
            singlepath.Add(endNode.ToString());          
            int pathNum = -1;
            int num = 0;
            while(pathNum !=startNode)
            {
                pathNum = Path[startNode, endNode];
                singlepath.Add(pathNum.ToString());
                endNode = pathNum;                
            }
            singlepath.Reverse();
            foreach (string item in singlepath)
            {
                num++;
                if (num == singlepath.Count)
                    Console.Write(item);
                else
                    Console.Write(item + "-->");
            }            
            Console.WriteLine("\n");   
        }
        #endregion
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Floyd算法
{
    class MyConsole
    {

        public static void Write(string strOriginal,int TrueLength, char chrPad)
        {
            string str = PadRightTrueLen(strOriginal, ref TrueLength, chrPad);
            Console.Write(str);
        }

        public static void WriteLine(string strOriginal, int TrueLength, char chrPad)
        {
            string str = PadRightTrueLen(strOriginal, ref TrueLength, chrPad);
            Console.WriteLine(str);
        }

        /// <summary>
        /// 获取真实长度
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns> 
        public static int TrueLength(string str)
        {
            int lenTotal = 0;
            int n = str.Length;
            string strWord = "";  //清空字符串
            int asc;
            for (int i = 0; i < n; i++)
            {
                strWord = str.Substring(i, 1);
                asc = Convert.ToChar(strWord);
                if (asc < 0 || asc > 127)      // 在0~127间字符长度加1,否则加2
                {
                    lenTotal = lenTotal + 2;
                }
                else
                {
                    lenTotal = lenTotal + 1;
                }
            }
            return lenTotal;
        }
        /// <summary>
        /// 得到正确的格式
        /// </summary>
        /// <param name="strOriginal"></param>
        /// <param name="maxTrueLength"></param>
        /// <param name="chrPad"></param>
        /// <returns></returns>
        
        private static string PadRightTrueLen(string strOriginal, ref int maxTrueLength, char chrPad)
        {
            string strNew = strOriginal;
            if (strOriginal == null || maxTrueLength <= 0)
            {
                strNew = "";
                return strNew;
            }
            int trueLen = TrueLength(strOriginal);
            if (trueLen > maxTrueLength)                    // 如果字符串大于规定长度 将规定长度等于字符串长度
            {
                for (int i = 0; i < trueLen - maxTrueLength; i++)
                {
                    maxTrueLength += chrPad.ToString().Length;
                }


            }
            else// 填充  小于规定长度 用‘ ’追加,直至等于规定长度
            {
                for (int i = 0; i < maxTrueLength - trueLen; i++)
                {
                    strNew += chrPad.ToString();
                }
            }
            return strNew;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Data.OleDb;

namespace Floyd算法
{
    class OpenExcel
    {
        public static double[,] ExcelData;
        public static double[,] GetDataFormExcel(string filepath)
        {
            string path = filepath;//获取打开文件的路径
            string strCon = @"Provider=Microsoft.Ace.OleDb.12.0;Data Source =" + path + ";" + "Extended Properties='Excel 8.0;HDR=NO;IMEX=1;'";
            OleDbConnection myConn = new OleDbConnection(strCon);
            string strCom = " select * from [Sheet1$] ";
            OleDbDataAdapter myCommand = new OleDbDataAdapter(strCom, myConn);
            DataSet myDataSet = new DataSet();
            myCommand.Fill(myDataSet);
            ExcelData = new double[myDataSet.Tables[0].Rows.Count, myDataSet.Tables[0].Columns.Count];
            for (int i = 0; i < myDataSet.Tables[0].Rows.Count; i++)
            {
                for (int j = 0; j < myDataSet.Tables[0].Columns.Count; j++)
                {
                    ExcelData[i, j] = Convert.ToDouble(myDataSet.Tables[0].Rows[i][j]);
                }
            }
            return ExcelData;

        }
    }
}


运行结果如下:

在这里插入图片描述

最后附上完成工程的链接:

Floyd算法【C#实现 可输出路径】

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值