【发布时间】:2021-07-29 11:23:26
【问题描述】:
数据结构中二叉树的中序、后序和前序遍历的时间复杂度是多少?是 O(n) 还是 O(log n) 还是 O(n^2)?
【问题讨论】:
-
哪些数据结构?树?什么类型的树?
-
@CommanderZ - 让我们不要开始分裂头发。
标签: time-complexity
数据结构中二叉树的中序、后序和前序遍历的时间复杂度是多少?是 O(n) 还是 O(log n) 还是 O(n^2)?
【问题讨论】:
标签: time-complexity
O(n),因为你遍历每个节点一次。或者更确切地说 - 您为每个节点所做的工作量是恒定的(不依赖于其余节点)。
【讨论】:
postorder 的复杂之处是什么?
对于任何订单,遍历都是 O(n) - 因为您要访问每个节点一次。如果树具有某种组织架构(即二叉搜索树),则查找可以小于 O(n)。
【讨论】:
O(n),我会说。 我正在做一棵平衡的树,适用于所有的树。 假设你使用递归,
T(n) = 2*T(n/2) + 1 ----------> (1)
T(n/2) 用于左子树,T(n/2) 用于右子树,'1' 用于验证基本情况。
在简化 (1) 时,您可以证明遍历(中序或前序或后序)的顺序为 O(n)。
【讨论】:
有序、前序和后序遍历是深度优先遍历。
对于 Graph,深度优先遍历的复杂度为 O(n + m),其中 n 是节点数,m 是边数。
由于二叉树也是一个图,这里同样适用。 每个深度优先遍历的复杂度都是 O(n+m)。
由于在二叉树的情况下,可以源自一个节点的边数限制为 2,因此二叉树中总边的最大数量为 n-1,其中 n 是节点的总数。
那么复杂度就变成了O(n + n-1),也就是O(n)。
【讨论】:
嗨
我今天在课堂上被问到这个问题,这是一个很好的问题!我将在这里进行解释,并希望在错误的地方对我更正式的答案进行审查或更正。 :)
@Assaf 的观察也是正确的,因为二叉树遍历递归地遍历每个节点一次。
但是!由于它是一种递归算法,因此您通常必须使用更高级的方法来分析运行时性能。在处理顺序算法或使用 for 循环的算法时,使用求和通常就足够了。所以,下面是对这个分析的更详细的解释,供好奇的人参考。
如前所述,
T(n) = 2*T(n/2) + 1
其中 T(n) 是在您的遍历算法中执行的操作数(按顺序、前序或后序都没有区别。
有两个T(n),因为中序、前序和后序遍历都在左右子节点上调用自己。因此,将每个递归调用视为一个T(n)。换句话说,**左 T(n/2) + 右 T(n/2) = 2 T(n/2) **。 “1”来自函数内的任何其他恒定时间操作,例如打印节点值等。 (老实说,它可能是 1 或任何常数,并且渐近运行时仍然计算为相同的值。解释如下。)。
这种递归实际上可以使用大师定理使用大 theta 来分析。所以,我会在这里应用它。
T(n) = 2*T(n/2) + 常数
其中常数是一些常数(可以是 1 或任何其他常数)。
使用Masters' Theorem,我们有T(n) = a*T(n/b) + f(n)。
所以,a=2, b=2, f(n) = constant 因为f(n) = n^c = 1,那么有c = 0 因为 f(n) 是一个常数。
从这里,我们可以看到 a = 2 和 b^c = 2 ^0 = 1。所以,a>b^c 或 2>2^0。所以,c 或 0
从这里我们有 T(n) = BigTheta(n^{logb(a)}) = BigTheta(n^1) = BigTheta(n)
如果您不熟悉 BigTheta(n),它与 O(n)“相似”(请耐心等待 :)),但它是“更严格的界限”或更严格的近似运行时。因此,BigTheta(n) 是最坏情况 O(n) 和最佳情况 BigOmega(n) 运行时。
我希望这会有所帮助。保重。
【讨论】:
T(n) = 2T(n/2)+ c
T(n/2) = 2T(n/4) + c => T(n) = 4T(n/4) + 2c + c
类似地 T(n) = 8T(n/8) + 4c+ 2c + c
....
....
最后一步... T(n) = nT(1) + c(2从0到h(树的高度)的幂的和)
所以复杂度是 O(2^(h+1) -1)
但是 h = log(n)
所以,O(2n - 1) = O(n)
【讨论】:
考虑一棵有 3 个节点的倾斜二叉树,分别为 7、3、2。对于像搜索 2 这样的任何操作,我们必须遍历 3 个节点,删除 2 也必须遍历 3 个节点,插入 1 也必须遍历,我们必须遍历 3 个节点。因此,二叉树的最坏情况复杂度为 O(n)。
【讨论】:
二叉树的深度优先遍历的顺序为 O(n)。
Algo -- <b>
PreOrderTrav():-----------------T(n)<b>
if root is null---------------O(1)<b>
return null-----------------O(1)<b>
else:-------------------------O(1)<b>
print(root)-----------------O(1)<b>
PreOrderTrav(root.left)-----T(n/2)<b>
PreOrderTrav(root.right)----T(n/2)<b>
如果算法的时间复杂度是 T(n),那么它可以写成 T(n) = 2*T(n/2) + O(1)。 如果我们应用反向替换,我们将得到 T(n) = O(n)。
【讨论】: