【问题标题】:number of paths in a binary tree with a given sum给定总和的二叉树中的路径数
【发布时间】:2016-12-23 14:00:27
【问题描述】:

我正在解决问题https://leetcode.com/problems/path-sum-iii/ 在这里我也简单提一下: 在二叉树中求总和 = 总和的路径数。路径不一定必须从根(叶)开始(结束)。只要路径向下,就应该被视为有效路径。

这是我的解决方案:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */

public class Solution {
    public int pathSum(TreeNode root, int sum) {
        int path = 0;
        if(root.val == sum)
            return 1;
        else if(root.left == null && root.right == null)
            return 0;
        if(root.left != null){
            path += pathSum(root.left, sum - root.val);
            path += pathSum(root.left, sum);
        }
        if(root.right != null){
            path += pathSum(root.right, sum - root.val);
            path += pathSum(root.right, sum);
        }
        return path;
    }
}

根据他们的系统,答案是 3,但对于以下输入,我得到的答案是 4:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

      10
     /  \
    5   -3
   / \    \
  3   2   11
 / \   \
3  -2   1

Return 3. The paths that sum to 8 are:

1.  5 -> 3
2.  5 -> 2 -> 1
3. -3 -> 11

我花了好几个小时试图解释为什么我的代码不起作用,但我无法找出问题所在。

抱歉问了一个幼稚的问题 :( 但这让我很生气!

【问题讨论】:

    标签: java binary-search-tree


    【解决方案1】:

    我不确定您的解决方案有什么问题,但我认为它不正确。一方面,如果您的根为 8,您将立即返回并仅将根计为解决方案。我会这样做:

    import java.util.ArrayList;
    
    public class Solution {
        public static int pathSum(TreeNode root, int sum) {
          return pathSum(root, sum, 0, new ArrayList<Integer>());
        }
    
        public static int pathSum(TreeNode root, int sum, int count, ArrayList<Integer> arr) {
            arr.add(root.val);
            int acc = 0;
            for (int i=arr.size()-1; i>=0; i--) {
              acc += arr.get(i);
              if (acc == sum)
                count++;
            }
            if(root.left != null)
                count = pathSum(root.left, sum, count, arr);
            if(root.right != null)
                count = pathSum(root.right, sum, count, arr);
            arr.remove(arr.size()-1);
            return count;
        }
    
      static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
        public TreeNode(int v) {
          this.val = v;
        }
      }
    
      public static void main(String[] args) {
        TreeNode root = new TreeNode(10);
        root.left = new TreeNode(5);
        root.right = new TreeNode(-3);
        root.left.left = new TreeNode(3);
        root.left.right = new TreeNode(2);
        root.right.right = new TreeNode(11);
        root.left.left.left = new TreeNode(3);
        root.left.left.right = new TreeNode(-2);
        root.left.right.right = new TreeNode(1);
        System.out.println(pathSum(root, 8));
      }
    }
    

    我们的想法是在递归遍历树时使用沿路径的值填充 aarray,确保在返回时删除元素。当您访问一个节点时,您必须考虑从该节点到根路径上的任何节点的所有总和。它们中的任何一个都可以增加您的参考价值。这个实现是 O(nlogn),当你遍历 n 个节点时,你遍历一个 len 数组直到 log(n)。

    【讨论】:

    • 我不敢相信我没有看到立即返回的错误。 :)
    【解决方案2】:

    你的代码不能满足这个约束:

    these nodes should be continuous.
    

    例如这棵树的(值10)和这棵树的(值-2),它们之和等于8。它不满足连续,所以它不能计数。

    很遗憾,您的代码无法过滤这种情况。

    另一种解决方案:

    public class Solution {
    public int pathSum(TreeNode root, int sum) {
        int path = traverse(root,sum);
        return path;
    }
    
    public int traverse(TreeNode root, int sum){
        int path = 0;
        if(root==null){
            return 0;
        }
        else{
            path += calcu(root,sum);
            path += traverse(root.left,sum);
            path += traverse(root.right,sum);
            return path;
        }
    }
    
    private int calcu(TreeNode root, int sum) {
        if(root==null){
            return 0;
        }
        else if(root.val==sum){
            return 1 + calcu(root.left,sum-root.val)+calcu(root.right,sum-root.val);
        }
        else{
            return calcu(root.left,sum-root.val)+calcu(root.right,sum-root.val);
        }
    }
    }
    

    解释:traverse这棵树并把每一个treeNode作为根节点,在连续的前提下找到目标路径。

    【讨论】:

    • 你只需要从这里return 1。否则,您最终会计算出不需要的 0。 else if(root.val==sum){ return 1 + calcu(root.left,sum-root.val)+calcu(root.right,sum-root.val); } 。想象 sum:8,一棵完全左倾斜的树。 10 -> 5 -> 3 -> 2 -> -2。找到正确的路径 5->3 后,递归会继续求和为 0,满足 2,-2 甚至会被算作另一条路径。
    • @Bandi Kishore 不,2--&gt;-2 不会被我的程序接受,因为它不是continuous。如果只是return 1,则存在错误。 Imagine sum:0, a completely left skewed tree. 0 -&gt; 5 -&gt; -5-&gt; 2 -&gt; -2,如果只是return 1,它将缺少0-&gt;5-&gt;-50-&gt;5-&gt;-5-&gt;2-&gt;-2。这不是我们想要的。在这种情况下,关于我的代码,5-&gt;-5 和 '2->-2' 将不被接受,因为它必须以 root 0 开头并且必须是 continuous
    • 不,您的代码仍将计为 2 --> -2。考虑相同的示例 10 -> 5 -> 3 -> 2 -> -2。当它到达节点 3 时,它看到 root.val==sum 为真,所以它转到 calcu(),左节点为 2,总和为 0。现在 root.val==sum 再次为假,所以它转到 calcu(),左节点即 -2 和总和为 (0-(Node 2)0 = -2。现在root.val==sum 将再次变为-2 == -2,它是真的,所以它返回 1。因此你最终会计算 2 和 -2。找到总和“0”本身的特殊情况可能会以不同的方式处理,否则这可能会混淆其他情况。
    • @Bandi Kishore,这个路径是5-&gt;3-&gt;2-&gt;-2而不是2-&gt;-2,当当前根是5时,有两个以root 5开头的路径,5-&gt;35-&gt;3-&gt;2-&gt;-2,当当前根是2 2-&gt;-2 路径将被处理。可以复制这段代码测试一下,已经被the leetcode problem which OP refer接受了
    • 不确定它是如何被接受的,我猜他们没有处理这种情况的测试用例。您检查输入 10 -> 5 ->3 -> 2-> -2 的输出并总和为 8。它将输出为 2 个路径而不是 1 个。这是相同代码的输出 - ideone.com/uim08g
    【解决方案3】:

    您的解决方案的问题在于,如果您处于新的内部路径中,您不会从初始总和开始。

    因此,当您移动内部路径时,您应该同时跟踪累计总和和原始总和。

    在下面找到您的算法的修改副本。

    public static class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;
    
        boolean visitedAsRoot = false;
    
        TreeNode(int x) {
            val = x;
        }
    }
    
    public static int pathSum(TreeNode root, int accomulate, int sum) {
        int path = 0;
        if (root.val == accomulate)
            return 1;
        else if (root.left == null && root.right == null)
            return 0;
        if (root.left != null) {
            path += pathSum(root.left, accomulate - root.val, sum);
            if (!root.left.visitedAsRoot) {
                root.left.visitedAsRoot = true;
                path += pathSum(root.left, sum, sum);
            }
        }
        if (root.right != null) {
            path += pathSum(root.right, accomulate - root.val, sum);
            if (!root.right.visitedAsRoot) {
                root.right.visitedAsRoot = true;
                path += pathSum(root.right, sum, sum);
            }
        }
        return path;
    }
    
    public static void main(String args[]) {
        TreeNode t1 = new TreeNode(3);
        TreeNode t2 = new TreeNode(-2);
        TreeNode t3 = new TreeNode(1);
        TreeNode t4 = new TreeNode(3);
        TreeNode t5 = new TreeNode(2);
        TreeNode t6 = new TreeNode(11);
        TreeNode t7 = new TreeNode(5);
        TreeNode t8 = new TreeNode(-3);
        TreeNode t9 = new TreeNode(10);
    
        t4.left = t1;
        t4.right = t2;
    
        t5.right = t3;
    
        t7.left = t4;
        t7.right = t5;
    
        t8.right = t6;
    
        t9.left = t7;
        t9.right = t8;
    
        System.out.println(pathSum(t9, 8, 8));
    }
    

    【讨论】:

    • 我认为它仍然行不通。想象一下我的树完全倾斜的情况。我需要的总和是 8。10 -> 5 -> 5 -> 3。输出应该是 1,但我想这会给出 2。
    • 对,部分节点重复访问,我修改了答案。
    • 抱歉,它仍然可能无法正常工作 :) 想象同样的完全左倾斜的树。 Sum = 8. 10 -> 5 -> 4 -> 3 -> 1。输出应该是 1,我猜它会显示 0。因为首先它会尝试 10、5、4、3 和 1。当它不能t 形式,它会将 1 标记为已访问并尝试。再次失败,因此将 3 标记为已访问再次尝试(但 1 已被标记,因此不会添加它。)现在,当它到达节点 4 时,它现在将去寻找子节点并尝试但这些节点已经被访问(3 ,1).
    • 不,它会起作用,关键是将节点标记为已访问意味着您不会再次从它开始计数作为起始节点;)..但您肯定会使用它来累积父节点.自己试试吧,我已经做过了:)
    【解决方案4】:

    您的解决方案的问题在于它还计算 10 - 2 = 8。其中10 是最顶部的根节点,-2 是底部叶子。它忽略了中间的所有路径。

    我设法用 tamperedSum 布尔值解决了这个问题。

    public static int pathSum(TreeNode root, int sum, boolean tamperedSum)  
    {
        int path = 0;
        if(root.val == sum)
            path = 1;
    
        if(root.left == null && root.right == null)
            return path;
    
        if(root.left != null){
            path += pathSum(root.left, sum - root.val, true);
            if (!tamperedSum)
                path += pathSum(root.left, sum, false);
        }   
        if(root.right != null){
            path += pathSum(root.right, sum - root.val, true);
            if (!tamperedSum)
                path += pathSum(root.right, sum, false);
        }   
        return path;
    }   
    

    tamperedSum 布尔值设置为 true,当我们从原始总和中减去(节点的)值时,在本例中为 8。

    我们会这样调用它:

    pathSum(root, sum, false)
    

    想法是,如果总和已被篡改,即路径上的节点值已被扣除,我们不再允许将其按原样传递到节点下方的分支。

    因此,每当我们从总和中减去节点值时,我们将tamperedSum 设置为truesum - root.value。之后,它下面的所有节点都不允许通过sum而不从中扣除它们的节点值。

    【讨论】:

    • 什么时候会进入if(!tamperedSum)?在所有情况下都是如此。
    • 能否详细说明 tamperedSum 变量及其背后的原因?
    • 能否请您详细说明 tamperedSum 变量及其背后的原因?另外,您如何认为代码最初将 10 和 -2 视为一条路径?
    • Bandi, tamperedSum 最初设置为 false。只有当一个节点从原始总和中减去它的值时,它才会变为真。
    • 深。希望编辑澄清它。我通过调试程序发现了问题。
    猜你喜欢
    • 2016-09-07
    • 2017-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    • 2018-08-10
    • 2016-03-17
    相关资源
    最近更新 更多