与树相关的递归

时间:2014-02-17 00:38:58

标签: java algorithm tree

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

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语句?

3 个答案:

答案 0 :(得分:1)

这是递归的好visualisation。基本上,当你调用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。希望这会有所帮助。

答案 1 :(得分:1)

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

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

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

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

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

我会添加显示逻辑的评论。

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);
    }
}

答案 2 :(得分:0)

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

让我们考虑一下这样的树:

  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