Algorithm 递归二叉树遍历的运行时复杂性

Algorithm 递归二叉树遍历的运行时复杂性,algorithm,recursion,time-complexity,binary-tree,big-o,Algorithm,Recursion,Time Complexity,Binary Tree,Big O,这是我的解决方案,给定一棵二叉树,你需要找到所有非直接链接节点的总和。“直接联系”是指亲子关系,这一点很清楚 我的解决方案 如果访问了当前节点,则不允许您访问下一级别的节点。但是,如果未访问当前节点,则您可以访问下一级别的节点,也可以不访问下一级别的节点 它通过了所有的测试。但是,这种递归二叉树遍历的运行时复杂性是多少。我认为这是2^n,因为在每个节点上,您都有两个选择,是否使用它,以及相应地,下一个级别,每个选择都有两个选择,依此类推 空间复杂性:不使用任何额外的存储空间,但由于这是一个递归实

这是我的解决方案,给定一棵二叉树,你需要找到所有非直接链接节点的总和。“直接联系”是指亲子关系,这一点很清楚

我的解决方案 如果访问了当前节点,则不允许您访问下一级别的节点。但是,如果未访问当前节点,则您可以访问下一级别的节点,也可以不访问下一级别的节点

它通过了所有的测试。但是,这种递归二叉树遍历的运行时复杂性是多少。我认为这是
2^n
,因为在每个节点上,您都有两个选择,是否使用它,以及相应地,下一个级别,每个选择都有两个选择,依此类推

空间复杂性:不使用任何额外的存储空间,但由于这是一个递归实现,因此使用堆栈空间,堆栈中的最大元素可能是树的高度,即n。那么
O(n)

public int rob(TreeNode root) {
        return rob(root, false);
    }

    public int rob(TreeNode root, boolean previousStateUsed) {

        if(root == null)
            return 0;

        if(root.left == null && root.right == null)
        {
            if(previousStateUsed == true)
                return 0;
            return root.val;
        }

        if(previousStateUsed == true)
        {
            int leftSumIfCurrentIsNotUsedNotUsed = rob(root.left, false);
            int rightSumIfCurrentIsNotUsed = rob(root.right, false);
            return leftSumIfCurrentIsNotUsedNotUsed + rightSumIfCurrentIsNotUsed;
        }
        else
        {
            int leftSumIfCurrentIsNotUsedNotUsed = rob(root.left, false);
            int rightSumIfCurrentIsNotUsed = rob(root.right, false);
            int leftSumIsCurrentIsUsed = rob(root.left, true); 
            int rightSumIfCurrentIsUsed = rob(root.right, true);
            return Math.max(leftSumIfCurrentIsNotUsedNotUsed + rightSumIfCurrentIsNotUsed, leftSumIsCurrentIsUsed + rightSumIfCurrentIsUsed + root.val);
        }

    }

您当前的递归解决方案将是
O(2^n)
。很明显,我们可以举一个例子:

接下来,让我们划掉交替的节点层:

对于剩余的节点,我们有大约
n/2
节点(这会有所不同,但在最坏的情况下,您始终可以删除交替层以获得至少
n/2-1
节点)。仅使用这些节点,我们就可以对它们进行任意组合,因为它们之间没有冲突。因此,我们可以确定,在最坏的情况下,这至少需要
Omega(2^(n/2))
时间。您可能会得到一个更严格的界限,但这会让您意识到您的解决方案无法很好地扩展

这个问题是一个非常常见的问题

您应该能够在这方面使用动态规划。我强烈推荐它。假设我们正在为node
i
找到解决方案。假设我们已经有了节点
i.left
i.right
的解决方案,还假设我们有了它们的子节点(
i
的孙子节点)的解决方案。对于
i
的max解决方案,我们现在有两个选项:

  • max sum(i.left)+max sum(i.right)

  • i.val+max sum(i.left.left)+max sum(i.left.right)+max sum(i.right.left)+max sum(i.right.right)


你最大限度地利用这些,这就是你对
i
的解决方案。您可以在当前程序中执行此自下而上的DP或使用备忘录。两者都应该有效。最好的部分是,现在您的解决方案是
O(n)

您当前的递归解决方案将是
O(2^n)
。很明显,我们可以举一个例子:

接下来,让我们划掉交替的节点层:

对于剩余的节点,我们有大约
n/2
节点(这会有所不同,但在最坏的情况下,您始终可以删除交替层以获得至少
n/2-1
节点)。仅使用这些节点,我们就可以对它们进行任意组合,因为它们之间没有冲突。因此,我们可以确定,在最坏的情况下,这至少需要
Omega(2^(n/2))
时间。您可能会得到一个更严格的界限,但这会让您意识到您的解决方案无法很好地扩展

这个问题是一个非常常见的问题

您应该能够在这方面使用动态规划。我强烈推荐它。假设我们正在为node
i
找到解决方案。假设我们已经有了节点
i.left
i.right
的解决方案,还假设我们有了它们的子节点(
i
的孙子节点)的解决方案。对于
i
的max解决方案,我们现在有两个选项:

  • max sum(i.left)+max sum(i.right)

  • i.val+max sum(i.left.left)+max sum(i.left.right)+max sum(i.right.left)+max sum(i.right.right)


你最大限度地利用这些,这就是你对
i
的解决方案。您可以在当前程序中执行此自下而上的DP或使用备忘录。两者都应该有效。最好的部分是,现在您的解决方案是
O(n)

谢谢。对于DP,您的意思是说,在您的响应的最后一行中,时间复杂度是O(n)还是O(n!)。我要去O(n)。@PepperBoy,是的
O(n)
。我只是热情地说:)谢谢。对于DP,您的意思是说,在您的响应的最后一行中,时间复杂度是O(n)还是O(n!)。我要去O(n)。@PepperBoy,是的
O(n)
。我只是热情地说:)