我发现 Prims 算法的时间复杂度无处不在O((V + E) log V) = E log V。但正如我们可以看到的算法:
时间复杂度似乎是O(V(log V + E log V))。但是如果它的时间复杂度是O((V + E) log V)。那么嵌套必须是这样的:
但是上面的嵌套似乎是错误的。
我发现 Prims 算法的时间复杂度无处不在O((V + E) log V) = E log V。但正如我们可以看到的算法:
时间复杂度似乎是O(V(log V + E log V))。但是如果它的时间复杂度是O((V + E) log V)。那么嵌套必须是这样的:
但是上面的嵌套似乎是错误的。
MST-PRIM(G, w, r)
1 for each u ∈ G.V
2 u.key ← ∞
3 u.π ← NIL
4 r.key ← 0
5 Q ← G.V
6 while Q ≠ Ø
7 u ← EXTRACT-MIN(Q)
8 for each v ∈ G.Adjacent[u]
9 if v ∈ Q and w(u, v) < v.key
10 v.π ← u
11 v.key ← w(u, v)
使用二叉堆
一次调用所需的时间复杂度EXTRACT-MIN(Q)
是O(log V)
使用最小优先级队列。第 6 行的 while 循环总共执行了 V次。EXTRACT-MIN(Q)
所以称为V
时间。所以 的复杂度EXTRACT-MIN(Q)
是O(V logV)
。
第for
8 行的循环正在执行总2E
次数,因为每个邻接列表的长度是2E
针对无向图的。执行第 11 行所需的时间是O(log v)
使用DECREASE_KEY
最小堆上的操作。第 11 行也执行总2E
次数。所以执行第 11 行所需的总时间是O(2E logV) = O(E logV)
.
第for
1 行的循环将被执行V
次数。使用该过程执行第 1 到 5 行将需要O(V)
.
的总时间复杂度MST-PRIM
是执行步骤 1 到 3 所需时间复杂度的总和O(VlogV + (E logV + V) = O(E logV)
。
使用斐波那契堆
O(1)
摊销时间。第 11 行总共执行了2E
几次。所以总时间复杂度为O(E)
。因此,总时间复杂度MST-PRIM
是执行步骤 1 到 3 的总复杂度,总复杂度为O(V logV + E + V)=O(E + V logV)
。
你的想法似乎是正确的。让我们将复杂性视为
V(lg(v) + E(lg(v)))
然后注意,在内部 for 循环中,我们实际上是遍历所有顶点,而不是边缘,所以让我们稍微修改一下
V(lg(v) + V(lg(v)))
这意味着
V(lg(v)) + V*V(lg(v))
但是对于最坏情况分析(密集图),V*V 是大致等于边数 E
V(lg(v)) + E(lg(v))
(V+E((lg(v))
但由于V << E
,因此
E(lg(v))
Prim 算法的时间复杂度为 O(VlogV + ElogV)。似乎您了解它是如何VlogV
产生的,所以让我们跳过它。那么ElogV
从哪里来呢?我们先来看一下 Prim 算法的源代码:
| MST-PRIM(Graph, weights, r)
1 | for each u ∈ Graph.V
2 | u.key ← ∞
3 | u.π ← NIL
4 | r.key ← 0
5 | Q ← Graph.V
6 | while Q ≠ Ø
7 | u ← EXTRACT-MIN(Q)
8 | for each v ∈ Graph.Adj[u]
9 | if v ∈ Q and weights(u, v) < v.key
10| v.π ← u
11| v.key ← weights(u, v)
对 中的每个元素执行第 8-11 行Q
,我们知道其中有V
元素Q
(表示所有顶点的集合)。第 8 行的循环正在遍历当前提取的顶点的所有邻居;我们将对下一个提取的顶点以及之后的一个顶点执行相同的操作。Djistkra 的算法不重复顶点(因为它是一个贪婪的最优算法),并且最终会让我们遍历每个连接的顶点,探索它们的所有邻居。换句话说,这个循环最终会在某个点(2E
)两次遍历图形的所有边。
为什么两次?因为在某些时候,我们会从另一个方向回到先前探索过的边缘,并且在我们真正检查它之前我们不能排除它。幸运的是,2
在我们的时间复杂度分析过程中,这个常数被删除了,所以循环实际上只是在做E
大量的工作。
为什么没有呢V*V
?如果您只考虑我们必须检查每个 Vertex 及其邻居,并且在最坏的情况下,图表的邻居数接近 ,您可能会达到该术语V
。确实,在密集图中V*V = E
。但这两个循环的工作更准确的描述是“两次遍历所有边缘”,因此我们E
改为引用。读者可以将他们的图的稀疏程度与该术语的时间复杂度联系起来。
让我们看一个有 4 个顶点的小示例图:
1--2
|\ |
| \|
3--4
假设这Q
将按 1、2、3 和 4 的顺序为我们提供节点。
总迭代次数为 10,是边数 ( 2*5
) 的两倍。
提取最小值并跟踪更新的最小边(通常使用斐波那契堆完成,导致log(V)
时间复杂度)发生在循环迭代内 - 确切的机制涉及最终需要在内循环内发生足够多的时间,以使它们受时间控制两个循环的复杂性。因此,算法这一阶段的完整时间复杂度为O(2*E*log(V))
。放弃常数产量O(E*log(V))
。
鉴于算法的总时间复杂度为O(VlogV + ElogV)
,我们可以简化为O((V+E)logV)
。在一个稠密的图E > V
中,我们可以得出结论O(ElogV)
。
实际上,正如您所说,for 嵌套在内部,而时间复杂度应该是 vE lg V 在渐近分析的情况下是正确的。但是在科门,他们已经做了摊销分析,这就是为什么它出来(Elogv)