如何在 Floyd-Warshall 算法中输出最短路径?

Posted

技术标签:

【中文标题】如何在 Floyd-Warshall 算法中输出最短路径?【英文标题】:How to output the shortest path in Floyd-Warshall algorithm? 【发布时间】:2011-05-30 10:05:35 【问题描述】:

我正在尝试实现 Floyd-Warshall 算法(所有对最短路径)。在下面的代码中,当我输入一些数字时,它会给出最后一个数字作为输入。我知道代码不完整。

现在我应该如何打印每个 i 和 j 的最短路径?或者你建议我做什么来完成这段代码。谢谢。

private void button10_Click(object sender, EventArgs e)


    string ab = textBox11.Text;
    int matrixDimention = Convert.ToInt32(ab);
    int[,] intValues = new int[matrixDimention, matrixDimention];
    string[] splitValues = textBox9.Text.Split(',');
    for (int i = 0; i < splitValues.Length; i++)
        intValues[i / (matrixDimention), i % (matrixDimention)] =    Convert.ToInt32(splitValues[i]);
    string displayString = "";
    for (int inner = 0; inner < intValues.GetLength(0); inner++)
    
        for (int outer = 0; outer < intValues.GetLength(0); outer++)
            displayString += String.Format("0\t", intValues[inner, outer]);
        displayString += Environment.NewLine;
    
    int n = (int)Math.Pow(matrixDimention, 2);
    string strn = n.ToString();

    MessageBox.Show("matrix"+strn+ "in" + strn + "is\n\n\n" +displayString);
////before this line i wrote the codes to get the numbers that user enter in textbox and put it in an 2d array
    for (int k = 1; k < n+1; k++)

        for (int i = 1; i < n+1; i++)

            for (int j = 1; j < n+1; j++)

                if (intValues[i, j] > intValues[i, k] + intValues[k, j])
                
                    intValues[i, j] = intValues[i, k] + intValues[k, j];
                    string str_intvalues = intValues[i, j].ToString();
                    MessageBox.Show("Shortest Path from i to j is: " + str_intvalues);

                
                else
                
                    string str_intvalues = intValues[i, j].ToString();
                    MessageBox.Show("Shortest Path from i to j is: " + str_intvalues);
                

【问题讨论】:

【参考方案1】:

为了在同一页面上,让我先向您展示 Floyd-Warshall 算法:

让我们有一个图,用矩阵D描述,其中D[i][j]是边(i -&gt; j)的长度(从图的索引为i的顶点到索引为j的顶点)。

矩阵D的大小为N * N,其中N是图中顶点的总数,因为我们可以通过将每个图的顶点相互连接来达到路径的最大值。

我们还需要矩阵R,我们将在其中存储最短路径(R[i][j] 包含最短路径中下一个顶点的索引,从顶点i 开始,到顶点j 结束)。

矩阵R 的大小与D 相同。

Floyd-Warshall 算法执行以下步骤:

    用边的结束顶点初始化图中任意两对或顶点之间的所有路径的矩阵(这很重要,因为该值将用于路径重建)

    对于每对连接的顶点(阅读:对于每条边(u -&gt; v)uv,找到顶点,它们之间形成最短路径:如果顶点k 定义了两条有效边(u -&gt; k)(k -&gt; v) (如果它们出现在图中),它们一起比路径(u -&gt; v) 短,然后假设@ 之间的最短路径987654346@和v通过k撒谎;将边 (u -&gt; v) 的矩阵 R 中的最短枢轴点设置为边 (u -&gt; k) 的对应枢轴点

现在我们与定义在同一页面上,算法可以这样实现:

// Initialise the routes matrix R
for (int i = 0; i < N; i++) 
    for (int t = 0; t < N; t++) 
        R[i][t] = t;
    


// Floyd-Warshall algorithm:
for (int k = 0; k < N; k++) 
    for (int u = 0; u < N; u++) 
        for (int v = 0; v < N; v++) 
            if (D[u, v] > D[u, k] + D[k, v]) 
                D[u, v] = D[u, k] + D[k, v];
                R[u, v] = R[u, k];
            
        
    

但是我们如何读取矩阵D

让我们有一个图表:

在 GraphViz 中会这样描述:

digraph G 
    0->2 [label = "1"];
    2->3 [label = "5"];
    3->1 [label = "2"];
    1->2 [label = "6"];
    1->0 [label = "7"];

我们首先创建一个大小为4 的二维数组(因为我们的图中正好有4 个顶点)

我们用零初始化它的主对角线(索引相等的项目,例如G[0, 0]G[1, 1] 等),因为 从顶点到自身的最短路径长度为0,而其他元素的数字非常大(表示它们之间没有边或无限长的边)。定义的元素,对应于图的边,我们用边的长度填充:

int N = 4;
int[,] D = new int[N, N];

for (int i = 0; i < N; i++) 
    for (int t = 0; t < N; t++) 
        if (i == t) 
            D[i, t] = 0;
         else 
            D[i, t] = 9999;
        
    


D[0, 2] = 1;
D[1, 0] = 7;
D[1, 2] = 6;
D[2, 3] = 5;
D[3, 1] = 2;

算法运行后,矩阵R 将填充顶点的索引,描述它们之间的最短路径。为了重构从顶点u到顶点v的路径,您需要遵循矩阵R的元素:

List<Int32> Path = new List<Int32>();

while (start != end)

    Path.Add(start);

    start = R[start, end];


Path.Add(end);

整个代码可以包含在几个方法中:

using System;
using System.Collections.Generic;

public class FloydWarshallPathFinder 
    private int N;
    private int[,] D;
    private int[,] R;

    public FloydWarshallPathFinder(int NumberOfVertices, int[,] EdgesLengths) 
        N = NumberOfVertices;
        D = EdgesLengths;
        R = null;
    

    public int[,] FindAllPaths() 
        R = new int[N, N];

        for (int i = 0; i < N; i++)
        
            for (int t = 0; t < N; t++)
            
                R[i, t] = t;
            
        

        for (int k = 0; k < N; k++)
        
            for (int v = 0; v < N; v++)
            
                for (int u = 0; u < N; u++)
                
                    if (D[u, k] + D[k, v] < D[u, v])
                    
                        D[u, v] = D[u, k] + D[k, v];
                        R[u, v] = R[u, k];
                    
                
            
        

        return R;
    

    public List<Int32> FindShortestPath(int start, int end) 
        if (R == null) 
            FindAllPaths();
        

        List<Int32> Path = new List<Int32>();

        while (start != end)
        
            Path.Add(start);

            start = R[start, end];
        

        Path.Add(end);

        return Path;
    


public class MainClass

    public static void Main()
    
        int N = 4;
        int[,] D = new int[N, N];

        for (int i = 0; i < N; i++) 
            for (int t = 0; t < N; t++) 
                if (i == t) 
                    D[i, t] = 0;
                 else 
                    D[i, t] = 9999;
                
            
        

        D[0, 2] = 1;
        D[1, 0] = 7;
        D[1, 2] = 6;
        D[2, 3] = 5;
        D[3, 1] = 2;

        FloydWarshallPathFinder pathFinder = new FloydWarshallPathFinder(N, D);

        int start = 0;
        int end = 1;

        Console.WriteLine("Path: 0", String.Join(" -> ", pathFinder.FindShortestPath(start, end).ToArray()));
    

您可以在 wikipedia 上阅读“关于此算法的信息”并获取自动生成的一些数据结构 here

【讨论】:

【参考方案2】:

当您使用 Floyd 算法时,它只保存图的节点 i 到节点 j 的最短距离。因此,您还可以保存节点的路径。怎么做?

实现它的方法之一是保存父节点(节点,它是路径中当前节点的前一个节点)节点。您将制作另一个矩阵,其中将包含路径。它可能看起来像这样:

int[,] pathS = new int[matrixDimention, matrixDimention];
for (int i = 0; i < splitValues.Length; i++)
    intValues[i / (matrixDimention), i % (matrixDimention)] = Convert.ToInt32(splitValues[i]);
    pathS[i / (matrixDimention), i % (matrixDimention)] = -1;    

.....
for (int k = 1; k < n+1; k++)
    for (int i = 1; i < n+1; i++)
        for (int j = 1; j < n+1; j++)
            if (intValues[i, j] > intValues[i, k] + intValues[k, j])                
                intValues[i, j] = intValues[i, k] + intValues[k, j];
                pathS[i,j] = k;
                string str_intvalues = intValues[i, j].ToString();
                MessageBox.Show("Shortest Path from i to j is: " + str_intvalues);
            
            else                
                string str_intvalues = intValues[i, j].ToString();
                MessageBox.Show("Shortest Path from i to j is: " + str_intvalues);
            

现在您有了额外的 pathS 数组,其中包含节点 i 和 j 之间的中间路径。为了更好地理解,您应该考虑, pathS[i,j] 是这两个节点之间的一个节点(例如 i -> [k] -> j)。但是您的路径可能比 3 个节点更长(这就是我在 [] 大括号中写节点 k 的原因)。所以,现在你必须检查 i 和 k 之间的路径 - pathS[i,k] 和 k 和 j 之间的路径 - pathS[k,j]。并递归执行相同操作,直到某些 i 和 j 之间的 pathS 等于“-1”。

【讨论】:

以上是关于如何在 Floyd-Warshall 算法中输出最短路径?的主要内容,如果未能解决你的问题,请参考以下文章

多源最短路径---Floyd-Warshall算法

Floyd-Warshall 算法:获得最短路径

Floyd-Warshall 算法返回具有相同权重的每条最短路径

多源最短路径--Floyd-Warshall算法

最短路径之Dijkstra算法和Floyd-Warshall算法

适用于负循环的 Floyd-Warshall 算法 [关闭]