无法弄清楚如何解决在二叉树中返回两个总和为目标值的值的逻辑缺陷

Posted

技术标签:

【中文标题】无法弄清楚如何解决在二叉树中返回两个总和为目标值的值的逻辑缺陷【英文标题】:Can't figure out how to address logic flaw in returning two values that sum to a target value in a Binary Tree 【发布时间】:2020-05-16 22:47:16 【问题描述】:

我正在解决以下问题:

给定一个二叉搜索树和一个目标编号,如果 BST 中存在两个元素且它们的和等于给定目标,则返回 true。

示例 1: 输入:

    5
   / \
  3   6
 / \   \
2   4   7

目标 = 9 输出:真

示例 2: 输入:

    5
   / \
  3   6
 / \   \
2   4   7

目标 = 28 输出:假

我写了以下代码:

class TreeNode 
    constructor(val, left, right) 
        this.val = (val === undefined ? 0 : val)
        this.left = (left === undefined ? null : left)
        this.right = (right === undefined ? null : right)
    


const findTarget = (root, k) => 
    const hash = 
    const values = []
    // let val1, val2 //amended to =>
    let val1 = null
    let val2 = null

    const dfs = (c) => 
        if (!c) return
        values.push(c.val)
        hash[c.val] = c.val
        if (c.left) dfs(c.left)
        if (c.right) dfs(c.right)
    
    dfs(root)
    for (let v of values) 
        // if (val1 && val2) return true //amended to =>
        if (val1 !== null && val2 !== null) return true
        if (hash[k - v] && !val1) val1 = v
        else if (hash[k - v] && val1) val2 = v
    
    // return !!(val1 && val2) //amended to =>
    return (val1 !== null && val2 !== null)


// These are the test cases:
const tree = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)))
const tree2 = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)))
const tree3 = new TreeNode(2, new TreeNode(0, new TreeNode(-4), new TreeNode(1)), new TreeNode(3))
const tree4 = new TreeNode(0, new TreeNode(-2, null, new TreeNode(-1)), new TreeNode(3, null, new TreeNode(4)))

console.log(findTarget(tree, 9)) //true
console.log(findTarget(tree2, 28)) //false
console.log(findTarget(tree3, -1)) //true
console.log(findTarget(tree4, -2)) //true but I return false

但是,最后一个返回 false 但它应该返回 true,我已经在白板上逐步完成它应该可以工作。我怀疑分配给 val1 或 val2 时的 0 没有通过真/假测试,但我不知道如何使 0 值返回真。至少这是我认为的解决方案,但不确定如何去做,而不是仅仅添加另一个或处理该条件的语句。

【问题讨论】:

0 是假的。所以你是对的,(val1 && val2) 将评估为 false 如果其中一个是 0 您可以实例化let val1 = null, val2 = null,然后改为检查val1 !== null && val2 !== null 你的建议很合理,我试过了,但它仍然返回错误。我不确定我是否遗漏了其他任何东西。 【参考方案1】:

表达式!val1 将是true,不仅当val1null 时,当它为0 时也是如此。在下一个if&& val2 也会发生类似的情况。同样,您应该将hash[k - v]undefined 进行比较。因此,当您将 if...else 块替换为:

        if (hash[k - v] !== undefined && val1 === null) val1 = v
        else if (hash[k - v] !== undefined && val1 !== null) val2 = v

或者,在else 部分保存一张支票:

        if (hash[k - v] !== undefined) 
            if (val1 === null) val1 = v;
            else               val2 = v;
        

我这里再给出一个算法,它不需要先收集hash中的所有值,而是进行两次中序遍历,一次从左到右(即非降序值),另一次右-向左(即非升序值)。当两个找到的值之和大于目标时,从右边来的遍历应该迈出一步。否则从左边来的遍历应该迈出一步。

class TreeNode 
    constructor(val, left=null, right=null) 
        this.val = val;
        this.left = left;
        this.right = right;
    
    * inorder(first, last) 
        if (this[first]) yield * this[first].inorder(first, last);
        yield this;
        if (this[last]) yield * this[last].inorder(first, last);
    
    find(k) 
        let asc = this.inorder("left", "right");
        let lo = asc.next();
        let desc = this.inorder("right", "left");
        let hi = desc.next();
        while (lo.value !== hi.value)  // while not the same node
             let sum = lo.value.val + hi.value.val;
             if (sum === k) return true;
             if (sum < k) lo = asc.next();
             else         hi = desc.next();
        
        return false;
    


const tree = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)))
const tree2 = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)));
const tree3 = new TreeNode(2, new TreeNode(0, new TreeNode(-4), new TreeNode(1)), new TreeNode(3));
const tree4 = new TreeNode(0, new TreeNode(-2, null, new TreeNode(-1)), new TreeNode(3, null, new TreeNode(4)));
console.log(tree.find(9))   // true
console.log(tree2.find(28)) // false
console.log(tree3.find(-1)) // true
console.log(tree4.find(-2)) // true

【讨论】:

我已经尝试了您解决方案的第一部分。但是,一个新的和最后一个测试用例失败了。 const tree5 = new TreeNode(0, new TreeNode(-3, new TreeNode(-4)), new TreeNode(2, new TreeNode(1)))console.log(findTarget(tree5, 1)) //true 的返回但我返回 false。不知道我错过了什么。 我是真的。您是否正确进行了更改?您是否不小心删除了循环中的第一个 if?看到它在jsfiddle上产生true 啊我的错误,如果我还有hash[k - v] !== undefined &amp;&amp; val1而不是hash[k - v] !== undefined &amp;&amp; val1 !== null。我会假设第一个不起作用,因为如果 val1 被分配 0,那么 val2 永远不会被分配,因为条件永远不会满足。 确实如此。就像你说的那样。【参考方案2】:

搜索相应哈希值时的 for 循环看起来有点可疑,尤其是 robinsax 观察到的 not 运算符的使用。建议简化为...

class TreeNode 
    constructor(val, left, right) 
        this.val = (val === undefined ? 0 : val);
        this.left = (left === undefined ? null : left);
        this.right = (right === undefined ? null : right);
    


const findTarget = (root, k) => 
    const hash = new Map();

    const dfs = (c) => 
        if (!c) return;
        hash.set( c.val, ( hash.get( c.val ) || 0 ) + 1 );
        if (c.left) dfs(c.left);
        if (c.right) dfs(c.right);
    
    dfs(root);
    
    for ( let v in  [...hash.keys()] ) 
      // Check to see if entry k-v exists.  Also, if v is the same as k-v then
      // ensure that there is more than one v.
      if ( hash.get( k - v ) && ( v == k - v ? 1 < hash.get( v ) : true ) ) 
        return true;
      
    ;

    return false;


const tree = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)));
const tree2 = new TreeNode(5, new TreeNode(3, new TreeNode(2), new TreeNode(4)), new TreeNode(6, null, new TreeNode(7)));
const tree3 = new TreeNode(2, new TreeNode(0, new TreeNode(-4), new TreeNode(1)), new TreeNode(3));
const tree4 = new TreeNode(0, new TreeNode(-2, null, new TreeNode(-1)), new TreeNode(3, null, new TreeNode(4)));
const tree5 = new TreeNode(1);
const tree6 = new TreeNode(1, new TreeNode(1), new TreeNode(2));

console.log(findTarget(tree, 9)); //true
console.log(findTarget(tree2, 28)); //false
console.log(findTarget(tree3, -1)); //true
console.log(findTarget(tree4, -2)); //true
console.log(findTarget(tree5, 2)); // false because 1 appears once
console.log(findTarget(tree6, 2)); // true because 1 appears twice

编辑添加了容纳重复节点的逻辑,以及测试用例树 5 和树 6。

希望这会有所帮助...

【讨论】:

之前没用过hasOwnProperty,看文档还是有点迷糊。和Object.keys(hash)中搜索k- v一样吗? @Altaf 是的,hash[c.val] = c.val 行有效地将新属性(或属性)添加到hash 对象,hasOwnProperty( k - v ) 函数只是查看是否存在相应的属性.例如,如果hash[ '2' ] 是被检查的当前值并且k = 9,那么hasOwnProperty( 7 ) 只是检查hash[ 7 ] 是否存在。 也尝试了这个解决方案,但是,const tree5 = new TreeNode(1) 的测试用例应该在console.log(findTarget(tree5, 2)) //false 时返回 false,但我返回 true @Altaf 很好的测试用例指出了我提出的算法中的一个缺陷!正在编辑修复。

以上是关于无法弄清楚如何解决在二叉树中返回两个总和为目标值的值的逻辑缺陷的主要内容,如果未能解决你的问题,请参考以下文章

如果不是树中的所有这些节点,Python会在二叉树中找到两个节点的最低共同祖先

在二叉树中找到两个节点的最近公共祖先

如何在二叉树中返回迭代遍历的迭代器?

在二叉树中找到两个节点最近公共祖先

使用递归 DFS 在二叉树中查找节点

在二叉树中查找给定节点的祖先