leetcode572判断是否子树空间复杂度

leetcode 572题, Subtree of Another Tree
approach1 (参考以下代码), 官方解答说space complextiy是O(M+N) :There will be at most N recursive call to dfs ( or isSubtree). Now, each of these calls will have M recursive calls to isIdentical. Before calling isIdentical, our call stack has at most O(N)elements and might increase to O(N+M)O(N+M) during the call. After calling isIdentical, it will be back to at most O(N) since all elements made by isIdentical are popped out. Hence, the maximum number of elements in the call stack will be M+N.

但是我觉得是O(N),N是root tree的nodes 数量,求举例解释space complexity是O(M+N)

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:

        # Check for all subtree rooted at all nodes of tree "root"
        def dfs(node):

            # If this node is Empty, then no tree is rooted at this Node
            # Thus "tree rooted at node" cannot be same as "rooted at subRoot"
            # "tree rooted at subRoot" will always be non-empty (constraints)
            if node is None:
                return False

            # If "tree rooted at node" is identical to "tree at subRoot"
            elif is_identical(node, subRoot):
                return True

            # If "tree rooted at node" was not identical.
            # Check for tree rooted at children
            return dfs(node.left) or dfs(node.right)

        def is_identical(node1, node2):

            # If any one Node is Empty, both should be Empty
            if node1 is None or node2 is None:
                return node1 is None and node2 is None

            # Both Nodes Value should be Equal
            # And their respective left and right subtree should be identical
            return (node1.val == node2.val and
                    is_identical(node1.left, node2.left) and
                    is_identical(node1.right, node2.right))

        # Check for node rooted at "root"
        return dfs(root)

图解leetcode572. 另一棵树的子树
使用双层递归,将深度优先搜索递归判断是否包含子树的方法抽取出来,再在主方法中递归每个节点的深搜即可。这种需要递归+遍历的推荐都写成两次递归的这种形式,不建议再额外写类似于leetcode437. 路径总和 III中树的遍历方法,时间复杂度O(m * n),空间复杂度O(m),

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null) return false;
        return dfs(root, subRoot) || isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot);
    }
    public boolean dfs(TreeNode root, TreeNode subRoot){
        if(root == null && subRoot == null) return true;
        if(root == null || subRoot == null) return false;
        if(root.val == subRoot.val) return dfs(root.left, subRoot.left) && dfs(root.right, subRoot.right);
        return false;
    }
}

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
在这里我们先简单介绍一下时间复杂度和空间复杂度的概念。时间复杂度是指在算法中基本操作执行次数的数量级,用来衡量程序的运行时间。而空间复杂度是指算法执行过程中所需存储空间的大小,用来衡量程序占用的空间大小。

在这个问题中,N表示主树的节点数,M表示子树的节点数。接下来看一下递归的代码:

        def dfs(node):

            if node is None:
                return False

            elif is_identical(node, subRoot):
                return True

            return dfs(node.left) or dfs(node.right)

我们可以看到在执行递归时,我们会在递归栈中不断地添加新的元素。因为我们需要不断地遍历整个主树和子树,所以最多可能会有N个元素进入递归栈。而在比较相应子树的时候,最多会有M个元素进入递归栈。因此在递归执行完毕后,递归栈的深度最多会有M+N个元素。因此,空间复杂度为O(M+N)。

至于为什么空间复杂度是O(M+N),你可以举一个简单的例子来说明:

假设主树的高度是N,而子树在主树某个节点处插入,这时子树的高度是M,那么递归是从主树的根节点开始的,在主树高度为a的节点下已经和子树相同,则递归基本结束,但是还需要回溯到主树的根节点再次递归到高度a+1的节点,查看是否和子树相同,依次递归下去,直到查完所有的节点,所以递归最多需要同时保存M+N个递归调用信息(M个是子树的高度,N个是主树的高度),因此空间复杂度是O(M+N)。

代码也很好解释:

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:

        def dfs(node):
            if node is None:
                return False

            elif is_identical(node, subRoot):
                return True

            return dfs(node.left) or dfs(node.right)

        def is_identical(node1, node2):
            if node1 is None or node2 is None:
                return node1 is None and node2 is None

            return (node1.val == node2.val and
                    is_identical(node1.left, node2.left) and
                    is_identical(node1.right, node2.right))

        return dfs(root)

如果我的回答解决了您的问题,请采纳!

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:

在这个问题中,空间复杂度的分析涉及到递归调用的栈空间。
在这个算法的实现中,递归函数 `dfs` 调用了 `is_identical` 函数,而 `is_identical` 函数又会递归地调用自己。
因此,在最坏情况下,递归栈的深度为两棵树的高度之和,即 $O(M+N)$,其中 $M$ 和 $N$ 分别是树 `root` 和树 `subRoot` 的节点数量。

在递归调用的过程中,每个递归栈帧需要存储一些状态信息,包括函数参数、局部变量和返回地址等。
因此,每个递归栈帧使用的空间大小是固定的,与树的节点数量无关。
因此,整个算法的空间复杂度可以看作是递归栈的空间复杂度,即 $O(M+N)$。


举一个例子来解释这个结论:
考虑树 `root` 有 $N$ 个节点,树 `subRoot` 有 $M$ 个节点,而且两棵树高度相同且每个节点都只有一个孩子。
此时,递归调用的过程中,最多有 $N$ 个递归栈帧存储树 `root` 的路径,同时最多有 $M$ 个递归栈帧存储树 `subRoot` 的路径。
因此,整个算法的空间复杂度为 $O(M+N)$。


希望这个例子可以帮助您更好地理解空间复杂度分析的过程。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

  • 以下回答由chatgpt基于相关博客总结生成:

    不能提供具体的实例来解释空间复杂度为什么是O(M+N),因为此问题所涉及的LeetCode 572题并非段落中提供的参考资料。对于参考资料中的各个算法解法,可以根据代码中开辟的变量空间进行分析。

    在LeetCode 572题的算法Approach1中,开辟的空间是res、q、p、todo、visited。其中,res、visited和q的空间复杂度都是N,p和todo的空间复杂度都是M。因此,算法Approach1的总空间复杂度是O(M+N)。如果仅考虑主树(即Res)的空间,则空间复杂度为O(N)。