【问题标题】:How to Compute Space Complexity for Binary SubTree Finding如何计算二叉子树查找的空间复杂度
【发布时间】:2015-06-07 04:49:33
【问题描述】:

这个问题来自Cracking the Coding Interview一书。我无法理解解决方案的空间复杂度。

问题:
你有两棵非常大的二叉树:T1,有数百万个节点,T2,有数百个节点。创建一个算法来确定 T2 是否是 T1 的子树。

解决方案(Java):

public static boolean containsTree(TreeNode t1, TreeNode t2) {
    if (t2 == null)
        return true; // The empty tree is a subtree of every tree.
    else
        return subTree(t1, t2);
}

/* Checks if the binary tree rooted at r1 contains the binary tree 
 * rooted at r2 as a subtree somewhere within it.
 */
public static boolean subTree(TreeNode r1, TreeNode r2) {
    if (r1 == null)
        return false; // big tree empty & subtree still not found.
    if (r1.data == r2.data) {
        if (matchTree(r1,r2)) return true;
    }
    return (subTree(r1.left, r2) || subTree(r1.right, r2)); 
}

/* Checks if the binary tree rooted at r1 contains the 
 * binary tree rooted at r2 as a subtree starting at r1.
 */
public static boolean matchTree(TreeNode r1, TreeNode r2) {
    if (r2 == null && r1 == null) 
        return true; // nothing left in the subtree
    if (r1 == null || r2 == null) 
        return false; //  big tree empty & subtree still not found
    if (r1.data != r2.data) 
        return false;  // data doesn’t match
    return (matchTree(r1.left, r2.left) && 
            matchTree(r1.right, r2.right));
}

书上说这个解决方案的空间复杂度O(log(n) +log(m)),其中m是节点数T1(更大的树)T2中的n个节点

对我而言,似乎解决方案具有 O(log(m)*log(n)) 空间复杂度,因为“子树”函数具有 log(n)递归调用,每个递归调用执行“matchTree”函数,触发 log(m) 递归调用。

为什么这个解是 O(log(n) + log(m)) 复杂度?

【问题讨论】:

    标签: java algorithm binary-tree space-complexity


    【解决方案1】:

    由于我们没有在堆上创建任何对象,空间复杂度就是堆栈的大小。所以问题不是发生了多少总调用,而是堆栈可以增长到多大。

    containsTree()只能调用subTree()subTree()可以调用自己或matchTree()matchTree()只能调用自己。所以在任何时候调用matchTree(),堆栈看起来像这样:

    [containsTree] [subTree] ... [subTree] [matchTree] ... [matchTree]
    

    这就是为什么您不在这里乘以空间复杂度的原因:虽然对 subTree() 的每次调用都可以调用 matchTree(),但对 matchTree() 的调用会在 subTree() 继续递归之前离开堆栈。

    按照“正确答案”的思路分析

    如果问题没有说明树是否平衡,那么真正的最坏情况分析会假设它们可能不是。然而,你和这本书都假设他们是。我们可以把这个问题放在后面,说T1的深度是cT2的深度是d时间>。如果 T1 是平衡的,则 cO(log(m)),否则为 O(m)T2d 也是如此。

    matchTree() 的最坏情况是 O(d),因为它可以递归的最远距离将是 T2 的高度。

    subTree() 的最坏情况是 O(c) 的递归,因为它可以递归的最远将是 T1 的高度,加上调用的成本matchTree(),总共O(c+d)

    containsTree() 只是在调用subTree() 之上添加一个常量,因此不会改变空间复杂度。

    所以如果T1T2都是平衡的,通过替换cd你可以看到O(log(m)+log(n)) 似乎是合理的。

    “正确答案”的问题

    就像我之前说的,在你知道二叉树是平衡的事实之前,假设二叉树是平衡的是不对的。所以更好的答案可能是O(m+n)

    但是等等!该问题指出 T2 的大小小于 T1 的大小。这意味着 nO(m),而 log(n)O(log(m))。那么为什么我们一直在浪费时间担心n

    如果树是平衡的,那么空间复杂度就是O(log(m))。在您不知道什么是平衡的一般情况下,真正的答案应该是 O(m),即较大树的大小。

    【讨论】:

    • 嗨,丹,对于 matchTree(),通过参考主定理,我们可以说 matchtree 的行为类似于 aT(n/2) + n,对于 n > 1,a = 1。主定理指出如果 a math.dartmouth.edu/archive/m19w03/public_html/Section5-2.pdf 找到。同样没有太多的数学分析,二叉树总是不平衡的,所有元素都是“线”的。在最坏的情况下 O(n )
    • 非常感谢您的回答。现在,我正在尝试应用您解释的逻辑来计算另一个类似问题的空间复杂度,但我仍然得出与书中解决方案不同的答案。你能看看下面的问题并解释我做错了什么吗?提前谢谢你。
    • @JuanZamora matchTree()不存在aT(n/2)+n的递归关系,因为matchTree()的栈空间是一个固定常数,不是n 的倍数。你是对的,如果树不平衡,它就会变成 O(n) 。由于 jjwest 和本书的作者都认为它 O(log(n)),我假设在书中的某个地方他们说“树木是平衡的”而 jjwest 只是忘记写了吗?跨度>
    • @jjwest on StackOverflow 最好将新问题作为新问题提出,而不是作为对问题的编辑。另外,你能确认他们说二叉树是平衡的吗?就像胡安说的,如果二叉树不平衡,最坏的情况是它的深度为 O(n)(即它相当于一个链表)。
    • 他们没有明确告诉我们树是平衡的,但他们根据他们的解决方案假设它是平衡的。谢谢。
    猜你喜欢
    • 2020-02-13
    • 2021-08-25
    • 1970-01-01
    • 2014-02-28
    • 2012-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-09
    相关资源
    最近更新 更多