如果你有一个图,需要找到它的直径(即两个节点之间的最大距离),如何在O(log v * (v + e))
复杂度内实现。
维基百科上说可以使用Dijkstra算法
和二叉堆
来实现。但我不明白这是如何工作的,有人能解释一下吗?
或者展示一个伪代码?
如果你有一个图,需要找到它的直径(即两个节点之间的最大距离),如何在O(log v * (v + e))
复杂度内实现。
维基百科上说可以使用Dijkstra算法
和二叉堆
来实现。但我不明白这是如何工作的,有人能解释一下吗?
或者展示一个伪代码?
虽然我已经晚了一年,但我不认为这些答案中有任何一个是正确的。OP在评论中提到边缘是无权重的;在这种情况下,最佳算法运行时间为O(nω log n)(其中ω是矩阵乘法的指数;目前由Virginia Williams上限限制在2.373)。
该算法利用了无权图的以下属性。让A成为具有每个节点自环的图的邻接矩阵。那么(Ak)ij为非零当且仅当d(i, j) ≤ k。我们可以利用这个事实通过计算Ak的log n个值来找到图直径。
算法如下:让A成为具有每个节点自循环的图的邻接矩阵。将M0 = A。当Mk包含至少一个零时,计算Mk+1 = Mk2。
最终,您会找到一个所有条目都非零的矩阵MK。根据上述属性,我们知道K ≤ log n;因此,我们迄今为止只进行了O(log n)次矩阵乘法。现在,我们可以继续在MK = A2K和MK-1 = A2K-1之间进行二进制搜索。以下是如何设置B等信息。
设置DIAMETER = 2k-1。对于i =(K-2 ... 0),执行以下测试:
将B乘以Mi,并检查结果矩阵是否为零。如果发现任何零,则将B设置为此矩阵乘积,并将2i添加到DIAMETER中。否则,不执行任何操作。
最后,返回直径。
作为一个次要的实现细节,可能需要在每次矩阵乘法执行后将矩阵中所有非零元素设置为1,这样值不会变得过大和难以在短时间内书写。对于一个一般的图 G=(V,E)
,目前没有已知时间复杂度为 O(log V * (V + E))
的算法来计算直径。
目前最好的解决方案是 O(V*V*V)
,比如使用 Floyd Warshall 算法计算所有最短路径。
对于稀疏图,即当 E
在 o(N*N)
时,Johnson 算法可以用 O(V*V*log(V)+V*E)
实现更好的时间复杂度。
如果您的图具有特定的性质,例如无环(树),则可以获得更好的效果。
所以坏消息是,在一般情况下 Dijkstra 不足以解决问题...
图形描述 - 无向且无权,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。我们使用队列Q执行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
现在分析这个算法,很容易看出它被第一步支配,该步骤的时间复杂度为 O(V*(V+E))
另一个时间复杂度为线性O(V+E)的算法: 1- 在任意随机顶点v∈V(G)上运行BFS 2- 选择距离最大的顶点u 3- 再次在那个顶点u上运行BFS 4- 直径是从步骤3生成的最大距离
http://www.boost.org/doc/libs/1_54_0/boost/graph/detail/sparse_ordering.hpp
由于直径可以通过遍历所有顶点的离心率来计算,因此可以使用O(V * E)的复杂度计算图的直径。
特别是对于一个非常稀疏的图,其中deg(G)<<
我没有研究Floyd Warshall算法。我只处理了一个具有> 5,000,000个顶点但最高顶点度数小于15的图,并认为这应该优于具有V * V * log(V)复杂度的算法。
编辑
当然,这仅适用于无向图,而不是负加权(甚至仅限于未加权?我现在不确定)
实际上,如果图形非常大,您将需要使用Dijkstra算法来查找最短距离。因此,这取决于OP的图形有多少个节点。
直径通常指图形G(V,E)中任意两个节点之间的最大最短距离,我们称之为δ(a,b)
。让d
成为BFS源的距离列表。让s
成为G中的任何节点。BFS(G,s)
将生成d值,其中d[a]
、d[b]
中有最大的d值。如果它们共享此值,则已找到a
和b
。否则,假设a
单独作为最大值。然后运行BFS(G,a)
,最大的d
值将是d[b]
,从而在O(V+E)+O(V+E)=O(V+E)
时间内找到δ(a,b)
。
这个方法的有效性在于第一次广度优先搜索将确定 a、b 或两者都是
。我们可以通过反证法来证明这是可能的。
证明更好地陈述在这个来源中:https://walkccc.me/CLRS/Chap22/22.2/
你不能只是单调地将 A 不断提高到下一个幂,并跟踪哪些 (i,j) 在某个 A^k 中具有非零条目吗?一旦所有的 i,j 在先前计算的 A^k 中都有了非零值,那么你所在的 k 就是图的直径。