【问题标题】:Time complexity of naïve merge of two binary search trees两个二叉搜索树的朴素合并的时间复杂度
【发布时间】:2018-01-31 16:42:30
【问题描述】:

我看到了一个很短的算法来合并两个二叉搜索树。我很惊讶它是多么容易而且效率很低。但是当我试图猜测它的时间复杂度时,我失败了。

让我们有两个包含整数的不可变二叉搜索树(不平衡),并且您希望将它们与伪代码中的以下递归算法合并在一起。函数insert为辅助:

function insert(Tree t, int elem) returns Tree:
    if elem < t.elem:
        return new Tree(t.elem, insert(t.leftSubtree, elem), t.rightSubtree)
    elseif elem > t.elem:
        return new Tree(t.elem, t.leftSubtree, insert(t.rightSubtree, elem))
    else
        return t

function merge(Tree t1, Tree t2) returns Tree:
    if t1 or t2 is Empty:
        return chooseNonEmpty(t1, t2)
    else
        return insert(merge(merge(t1.leftSubtree, t1.rightSubtree), t2), t1.elem)

我猜它是一个指数算法,但我找不到它的论据。这个合并算法最差的时间复杂度是多少?

【问题讨论】:

  • 为什么说算法无效?
  • @displayName 将一棵树插入另一棵树将是 O(n^3)。但这可能就像 O(n^n),因为对于每个节点,它会再次遍历整个树到底部。然后再一次(第二次合并)......
  • @greybeard 是的,“低效”是一个更好的词,我改了标题。我在“可用于有用的工作”的含义内使用“有效”一词,而不是作为可计算性理论的术语。

标签: algorithm merge time-complexity binary-search-tree asymptotic-complexity


【解决方案1】:

让我们考虑一下最坏的情况:

在每个阶段,每棵树都处于最大不平衡状态,即每个节点至少有一个大小为 1 的子树。

在这种极端情况下,insert 的复杂度很容易显示为Ө(n),其中n 是树中元素的数量,因为高度是~ n/2


基于上述约束,我们可以推导出时间复杂度为merge的递归关系:

其中n, mt1, t2 的大小。不失一般性地假定右子树总是包含单个元素。术语对应:

  • T(n - 2, 1):在t1 的子树上对merge 的内部调用
  • T(n - 1, m):在t2 上对merge 的外部调用
  • Ө(n + m):最后一次调用insert

为了解决这个问题,让我们重新替换第一项并观察一个模式:

我们可以通过去掉第一项来解决这个和:

在步骤(*) 中,我们使用了变量更改替换i -&gt; i + 1。当k = n:

时递归停止

T(1, m) 只是将一个元素插入到大小为m 的树中,这在我们假设的设置中显然是Ө(m)

因此,merge绝对最坏情况时间复杂度为


注意事项:

  • 参数的顺序很重要。因此,将较小的树插入到较大的树中是很常见的(在某种意义上)。
  • 实际上,在过程的每个 阶段,您极不可能拥有最大程度的不平衡树。一般情况下自然会涉及到半平衡树。
  • 最佳情况(即始终完美平衡的树)要复杂得多(我不确定是否存在上述分析解决方案;请参阅gdelab 的回答)。

编辑:如何评估指数和

假设我们要计算总和:

其中a, b, c, n 是正常数。在第二步中,我们将基数更改为 enatural 指数常数)。通过这种替换,我们可以将ln c 视为变量x,对其进行几何级数,然后设置x = ln c

但几何级数有一个封闭形式的解(一个不难推导的标准公式):

因此我们可以将这个结果相对于x 微分n 次以获得Sn 的表达式。对于上面的问题,我们只需要前两个幂:

所以这个麻烦的术语是:

这正是 Wolfram Alpha 直接引用的内容。如您所见,这背后的基本思想很简单,尽管代数非常乏味。

【讨论】:

  • 哇,非常感谢您的解决方案!巧妙地处理所有这些款项!我被困在后面的第二个等式中,你正在摆脱 1 和 n-2 之间的 2^(j-1) * Omega(...) 的总和。我不知道你从哪里得到 11*2^(n-2)+...。
  • @MartinJiřička 啊,是的,这个总和可以很容易地用数学技巧来完成,但我懒得明确地做到这一点,所以我只使用了 Wolfram Alpha :D 抱歉造成混淆;如果您希望我说明如何计算这样的总和,请告诉我
  • 是的,我很感兴趣!如果它有一些技巧,你只能发布一个名字,我会尝试自己计算它。谢谢! (如果它是正确的,我会将您的解决方案标记为答案;-D)
  • @MartinJiřička 完成。如果您还有什么不明白的,请告诉我
  • 很抱歉,我花了这么长时间才查看您的答案。老实说,我无法完全遵循您的解决方案,这对我来说太高了。无论如何,谢谢你的解释!
【解决方案2】:

精确计算非常困难,但在最坏的情况下它看起来不是多项式有界的(但这不是一个完整的证明,你需要一个更好的证明):

  • insert 在最坏的情况下具有复杂性O(h),其中h 是树的高度(即至少log(n),可能是n)。

  • merge() 的复杂性可能是以下形式:T(n1, n2) = O(h) + T(n1 / 2, n1 / 2) + T(n1 - 1, n2)

  • 让我们考虑F(n) 这样F(1)=T(1, 1)F(n+1)=log(n)+F(n/2)+F(n-1)。我们大概可以证明F(n) 小于T(n, n)(因为F(n+1) 包含T(n, n) 而不是T(n, n+1))。

  • 我们有F(n)/F(n-1) = log(n)/F(n-1) + F(n/2) / F(n-1) + 1

  • 假设某些kF(n)=Theta(n^k)。然后是F(n/2) / F(n-1) &gt;= a / 2^k 一些a&gt;0(来自Theta 中的常量)。

  • 这意味着(超出某个点n0)我们总是有F(n) / F(n-1) &gt;= 1 + epsilon 用于一些固定的epsilon &gt; 0,它与F(n)=O(n^k) 不兼容,因此是矛盾的。

    李>
  • 所以F(n) 不是任何kTheta(n^k)。直观地,您可以看到问题可能不是Omega 部分而是big-O 部分,因此它可能不是O(n)(但从技术上讲,我们在这里使用Omega 部分得到a)。由于T(n, n) 应该比F(n) 更大,T(n, n) 不应该是多项式,而可能是指数...

但话说回来,这根本不严谨,所以也许我真的错了......

【讨论】:

  • 嗯,我需要一些时间来仔细研究一下……Theta 和 Omega 是复杂性理论中众所周知的函数? (我没有听过他们。)
  • Omega 类似于 big-O 的倒数,且 F=Theta(g) 当且仅当 F=O(g) 且 F=Omega(g)。例如,请参阅here
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多