繁体   English   中英

与树相关的递归

[英]Recursion related to trees

这是找到等于特定总和的根到叶路径的代码:

public boolean hasPathSum(TreeNode root, int sum) {
    if (root == null) {
        return false;
    }  
    if (root.left==null && root.right==null) {
        return (sum == root.val);
    }   
    return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

即使一个递归调用返回true( return (sum==root.val) ),我也不明白原始函数调用是如何成立的。

我的理解是在堆栈中,该特定的激活记录是真的,但是然后堆栈上的其他调用不会返回false; 显然剩余的可能不是一条路径,并不会使它全部变为虚假? 它如何重视if语句?

这是一个很好的递归可视化 基本上,当你调用hasPathSum时,它会检查root是否为null。 如果它为null,则返回false。

如果root不为null,那么它会更进一步。 如果左右两个都为空,则表示您处于叶节点。 如果叶节点具有与根相同的值,那么您将返回true。 否则就是假的。

如果两个if语句都被跳过,则意味着左侧或右侧(或两者)具有更多节点。 然后根节点将成为你的左边和右边,你将检查那里的和值,并返回它们的结果。

让我们假设这是你的树,leaf4有所需的值:

            root
     left           right
leaf1    -       leaf3  leaf4  


----------- 1st depth, with root node ---------------
hasPathSum(root)
root==null //false, so it moves on
root.left // is 'left', so skipping
hasPathSum(left) || hasPathSum(right) // this statement will be evaluated

------------- 2nd depth, with left node ---------------
hasPathSum(left)
left==null //false, so it moves on
left.left // is 'leaf1', so skipping
hasPathSum(leaf) || hasPathSum(null) // this statement will be evaluated

------------- 3rd depth, with leaf1 node ---------------
hasPathSum(leaf1)
leaf1==null //false, so it moves on
leaf1.left and leaf1.right // are both null, so returnin with sum == root.val

------------- 3rd depth, with - node ---------------
hasPathSum(-)
-==null //true, so it returns with false

------------- 2nd depth, with left node ---------------
false || false // is false, so it will return with false

------ in this moment, hasPathSum(left) part of 1st depth's has been evaulated to false
so hasPathSum(right) has to be ecaluated as well.

它与上面的代码没有任何不同,除了处理leaf4时,sum == root.val将为true,因此整个事件将返回true。 希望这可以帮助。

实际上这没有以最清晰的方式编码。

递归始终是通过使用相同的过程(函数)来解决同一问题的一个或多个较小版本,然后组合这些解决方案来解决问题。

在这种情况下,较小的问题是检查左右子树中剩余的所需总和(如果存在)。

如果成功,我们可以在左侧停止,跳过右侧。 以这种方式,找到具有所需总和的树中的“最左边”路径。 我们没有必要找到任何其他人。

检查子树时,我们从所需的总和中减去当前节点的值。 直观地,这使得问题“如上所述”更小。

我将添加显示逻辑的注释。

public boolean hasPathSum(TreeNode root, int sum) {
    // If we've reached a null child, the other child is non-null,  so we're
    // not at a leaf, so there no way this can be a leaf-to-path sum.
    // See below for why this is the case.
    if (root == null) {
        return false;
    }
    // If we're at a leaf (null children), then we've found the path
    // if and only if the node value exactly equals the sum we're looking for. 
    if (root.left == null && root.right == null) {
        return (sum == root.val);
    }
    // We're not at a leaf.  See if we can find the remaining part of the sum
    // by searching the children.  Null children are handled above.  If the
    // sum is found in the left subtree, the short-circuit evaluation of ||
    // will skip searching the right.
    return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}

请注意,它可能没有意义

hasPathSum(null, 0)

在此代码中返回false。 我这样做:

class TreeNode {
    // ... skipping other TreeNode fields.
    public boolean isLeaf() { return left == null && right == null; } 
    public boolean hasPathSum(int sum) {
        return isLeaf() ? sum == val : 
            (left != null && left.hasPathSum(sum - val)) ||
            (right != null && right.hasPathSum(sum - val);
    }
}

解释一个简单的例子可能有所帮

让我们考虑这样一棵树:

  5
 / \
2   3
     \
      1

我们正在寻找9的总和。

现在递归调用将如下所示:
(我的缩进是这样的,每个语句都由前一个缩进级别的函数执行)

hasPathSum(N5, 9)
   hasPathSum(N2, 9-5 = 4)
      return false // since 2 != 4
   hasPathSum(N3, 9-5 = 4)
      hasPathSum(null, 4-3 = 1) // left child of N3
          return false // since root == null
      hasPathSum(N1, 4-3 = 1)
          return true // since 1 == 1
      return (false || true) = true
   return (false || true) = true

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM