图的直径算法?

时间:2013-03-26 20:01:12

标签: algorithm graph language-agnostic graph-algorithm

如果你有一个图表,并且需要找到它的直径(这是两个节点之间的最大距离),你怎么能在O(log v * (v + e))复杂度中做到这一点。

维基百科说,您可以使用Dijkstra's algorithm binary heap来执行此操作。 但我不明白这是如何运作的。有人可以解释一下吗?

或显示伪代码?

9 个答案:

答案 0 :(得分:8)

对于一般图表G=(V,E),没有已知用于计算直径的O(log V * (V + E))时间复杂度算法。 当前最佳解决方案是O(V*V*V),例如,通过使用Floyd Warshall算法计算所有最短路径。 对于稀疏图,即当E位于o(N*N)时,约翰逊算法为您提供O(V*V*log(V)+V*E)更好的时间复杂度。

如果您的图表具有非循环(树)等特定属性,则可以更好。

所以坏消息是,Dijkstra在一般情况下还不够......

答案 1 :(得分:3)

我知道我已经迟到了一年,但我不相信这些答案都是正确的。 OP在评论中提到边缘未加权;在这种情况下,最好的算法运行在$ O(n ^ {\ omega})\ log n $ time(其中$ \ omega $是矩阵乘法的指数;目前上限由弗吉尼亚威廉姆斯以2.373美元计算)。

该算法利用了未加权图的以下属性。设$ A $是图的邻接矩阵,为每个节点添加一个自循环。然后$(A ^ k)_ {ij} $非零iff $ d(i,j)\ le k $。我们可以通过计算$ A ^ k $的$ \ log n $值来查找图形直径。

以下是算法的工作原理:让$ A $成为图的邻接矩阵,为每个节点添加一个自循环。设置$ M_0 = A $。虽然$ M_k $至少包含一个零,但计算$ M_ {k + 1} = M_ {k} ^ 2 $。

最终,您会找到包含所有非零条目的矩阵$ M_ {K} $。我们知道上面讨论的财产是$ K \ le \ log n $;因此,到目前为止,我们只执行矩阵乘法$ O(\ log n)$次。我们现在可以继续在$ M_ {K} = A ^ {2 ^ K} $和$ M_ {K-1} = A ^ {2 ^ {K-1}} $之间进行二进制搜索。设置$ B = M_ {K-1} $,如下所示。

设置DIAMETER = $ 2 ^ {k-1} $。对于$ i =(K-2 \ dots 0)$,执行以下测试:

将$ B $乘以$ M_ {i} $并检查结果矩阵是否为零。如果我们找到任何零,则将$ B $设置为此矩​​阵产品,并将$ 2 ^ i $添加到DIAMETER。否则,什么也不做。

最后,返回DIAMETER。

作为次要的实现细节,可能需要在执行每个矩阵乘法后将矩阵中的所有非零条目设置为$ 1 $,这样这些值不会太大而且难以记录少量时间。

答案 2 :(得分:1)

Boost BGL有一个名为“rcm_queue”的小型扩展deque类,通过简单的广度优先搜索可以找到顶点的偏心率,这意味着O(E)的复杂性。

http://www.boost.org/doc/libs/1_54_0/boost/graph/detail/sparse_ordering.hpp

由于可以通过遍历所有顶点的偏心率来计算直径,因此可以计算复杂度为O(V * E)的图的直径。

特别是对于具有deg(G)<<<<<<< V,我没有找到更好的运行时间。

我没有研究Floyd Warshall算法。我刚刚用>处理图表5.000.000顶点,但任何顶点的最大程度小于15,并且认为这应该优于具有V * V * log(V)复杂度的算法。

修改

当然,这仅适用于无向图,而不是负加权(甚至只是未加权?我不确定atm)

答案 3 :(得分:1)

正如eci所提到的,其中一个解决方案是使用Floyd-Warshall算法。如果您需要它的代码,可以找到它的C ++版本here

答案 4 :(得分:0)

首先,您需要找到convex hull of the graph(找到它是O(nh),其中h是船体上的节点数)。直径点将位于凸包上,因此问题将减少,以找到h点中的最远点。因此,总订单将为O(nh + h * h)。

答案 5 :(得分:0)

实际上,如果图形非常大,则需要使用Dijkstra算法才能找到最短距离。所以这取决于OP图表有多少个节点。

答案 6 :(得分:0)

假设图形未加权,则以下步骤可以在O(V *(V + E))中找到解,其中V是顶点数,E是边数。

让我们定义2个顶点之间的距离u,v是u和v之间最短路径的长度。用d(u,v)表示 将顶点v的偏心定义为e(v)= max {d(u,v)| u∈V(G)}(V(G)是图G中的顶点集) 定义直径(G)= max {e(v)| v∈V(G)}

现在算法:

1-对于V(G)中的每个顶点v,运行BFS(v)并构建从每个顶点到其他顶点的2维距离数组。 (计算从每个顶点到其他顶点的距离很容易在Read this part about diameter in Wikipedia

中进行

2-计算步骤1中创建的二维数组中每个顶点的e(v) 通过从步骤2中找到最大e(v)来计算直径(G)

现在分析这个算法,很容易看出它的第一步是O(V *(V + E))

Working with SSH key passphrases

另一种以线性时间O(V + E)运行的算法 1-在任意随机顶点上运行BFSv∈V(G) 2-选择具有最大距离的顶点u 3-再次在该顶点上运行BFS u 4-直径是从步骤3生成的最大距离

答案 7 :(得分:0)

图表描述 - 无向和未加权, n 节点, m 边缘 对于图的直径,我们需要计算所有节点对之间的最短路径。可以使用BFS算法针对无向和未加权图来计算源节点与所有其他节点之间的最短路径。 1个节点的时间复杂度为 O(n + m)。这里因为我们需要为n个节点做BFS,所以在所有节点对之间找到最短路径的总时间复杂度变为 O(n(n + m))。由于存在 n(n-1)/ 2 个节点对,因此我们有所有节点之间最短路径的 n(n-1)/ 2 值。对于直径,我们需要得到这些值的最大值,也就是O(n * n)。所以最终的时间复杂度变为:

       O(n(n + m)) + O(n*n) = O(n(2n + m)) = O(n(n + m))

用于获取所有节点对之间的最短路径的伪代码。我们使用名为Shortest_paths的矩阵来存储任意两对节点之间的最短路径。我们使用名为flag的字典,其值为true或false,表示是否已访问过顶点。对于BFS的每次迭代,我们将此字典初始化为所有false。我们使用Queue Q来执行我们的BFS。 算法BFS(适用于所有节点)

Initialize a matrix named Shortest_paths[n][n] to all -1. 
    For each source vertex s
        For each vertex v
            Flag[v] = false
        Q = empty Queue
        Flag[s] = true
        enQueue(Q,s)
        Shortest_paths[s][s] = 0
        While Queue is not empty
            Do v = Dequeue(Q)
            For each w adjacent to v
                Do if flag[w] = false
                    Flag[w] = true
                    Shortest_paths[s][w] = Shortest_paths[s][v] + 1
                    enQueue(Q,w) 
    Diameter = max(Shortest_paths)
    Return Diameter

答案 8 :(得分:-1)

这只能在未加权的图表中发生。其中bfs在o(v + e)中给出最短路径树,并且对v源重复相同。