其他分享
首页 > 其他分享> > 如何计算二进制子树查找空间复杂度

如何计算二进制子树查找空间复杂度

作者:互联网

这个问题来自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))的复杂性?

解决方法:

由于我们没有在堆上创建任何对象,因此空间复杂度就是堆栈的大小.所以问题不在于有多少总呼叫发生,而是堆栈有多大.

containsTree()只能调用subTree(),subTree()可以调用自身或matchTree(),而matchTree()只能调用自身.因此,在调用matchTree()的任何时候,堆栈如下所示:

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

这就是为什么你不在这里乘以空间复杂性的原因:每次调用subTree()都可以调用matchTree(),那些对matchTree()的调用会在subTree()继续递归之前离开堆栈.

按照“正确答案”的分析

如果问题没有说明树是否平衡,那么真正的最坏情况分析会假设它们可能不是.但是,你和这本书都假设它们是.我们可以稍后将这个问题放在一边,说T1的深度为c,T2的深度为d.如果T1是平衡的,则c是O(log(m)),否则是O(m).对于T2来说同样的事情.

matchTree()的最坏情况是O(d),因为它可以递归到最远的是T2的高度.

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

并且containsTree()只是在调用subTree()时添加一个常量,这样就不会改变空间复杂度.

因此,如果T1和T2都是平衡的,通过替换c和d可以看出O(log(m)log(n))似乎是合理的.

“正确答案”的问题

就像我之前说过的那样,假设二元树是平衡的,直到你知道它们是真实的,这是不对的.所以更好的答案可能是O(m n).

可是等等!问题是T2的大小小于T1的大小.这意味着n是O(m),log(n)是O(log(m)).那么为什么我们一直在浪费时间担心n?

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

标签:java,algorithm,binary-tree,space-complexity
来源: https://codeday.me/bug/20190519/1137404.html