树遍历,传递的变量在退出最终递归时重置为0(Javascript)

Posted

技术标签:

【中文标题】树遍历,传递的变量在退出最终递归时重置为0(Javascript)【英文标题】:Tree traversal, passed variable is reset to 0 when exiting the final recursion (Javascript) 【发布时间】:2022-01-07 11:18:42 【问题描述】:

谁能给我解释一下为什么给定这段代码来遍历二叉树:

var sumOfLeftLeaves = function(root) 
    let sum = 0;
    traverse(root, sum);
    return sum;
;


function traverse(root, sum) 

    const left = root.left;
    const right = root.right;


    if (left) 
        sum += left.val;
        traverse(left, sum);
    

    if (right) 
        traverse(right, sum);
    


在树的遍历过程中已经正确计算的和,在退出最后一个递归阶段后重置为0?

【问题讨论】:

【参考方案1】:
var sumOfLeftLeaves = function(root) 
    let sum = 0;
    return traverse(root, sum);
;


function traverse(root, sum) 

    const left = root.left;
    const right = root.right;

    sum += root.val
    if (left) 
        sum += traverse(left, sum);
    

    if (right) 
        sum += traverse(right, sum);
    
    return sum


sum 是一个不可变的值。这意味着每次将它作为参数传递给函数调用时,都会生成一个新副本。因此,您在递归堆栈中进一步向下添加的内容不会反映在上述堆栈帧中的sum 变量上。

进一步阐述sum 是一个整数,在 JS 术语中Number。它是一种值类型,这意味着在每次赋值时,它都是按值传递、复制过来的。换句话说,JS 运行时在堆栈上创建一个新位置并将分配的值存储在新位置。您对值类型所做的任何突变/修改都不会反映在原始变量上。

这种方法解决了问题。

另一种方法是删除 sum 作为参数,只返回节点值的总和,如下所示:

var sumOfLeftLeaves = function(root) 
    return traverse(root);
;


function traverse(root) 
    if (root == null) return 0

    return root.val + traverse(root.left) + traverse(root.right);

为了完整起见,第三种方法是将sum 设为可变变量,如 js 对象类型或数组。虽然对象或数组的结构本身是按值传递的,但它们的内容可以更改,这些更改将反映在原始变量中。假设您将sum 作为一个元素的数组传递,const sum = [0]。每当您向其中添加内容时,sum[0] += node.valsum 的索引为零处的元素的值会发生变化,并且对于调用堆栈上方的函数也可见。

【讨论】:

那么为什么 sum 是不可变的呢?你能详细说明一下吗? 当然,我已将其添加到答案中,因为它不适合 cmets。 非常感谢! 我在最后添加了一些 cmets,如果您想进一步测试和探索,可能会有所帮助。 我真的很困惑,因为我通常不使用 javascript 而是 typescript。现在我不确定我是否曾经在递归函数中使用过数字并修改了原始数字或者它是一个对象。我不确定这两种语言在这方面是否有区别,或者我只是愚蠢。可能是后者【参考方案2】:

您不会从traverse 返回sum,也不会在sumOfLeftLeaves 中重新分配它。你可能想要更多这样的东西:

var sumOfLeftLeaves = function(root) 
    return traverse(root, 0);
;


function traverse(root, sum) 
    const left = root.left;
    const right = root.right;

    if (left) 
        sum += left.val;
        return traverse(left, sum);
    

    if (right) 
        return traverse(right, sum);
    

    return sum;

【讨论】:

【参考方案3】:

您可以采用一个函数,它返回值加上左右两侧或零。

const
    sumOfTree = node => node
        ? value + sumOfTree(node.left) + sumOfTree(node.right)
        : 0;

另一种解决方案可能是使用遍历函数,该函数接受一个节点和一个函数,该函数带有一个对象的闭包作为值。

const
    traverse = (node, fn) => 
        if (!node) return;
        fn(node);
        traverse(node.left, fn);
        traverse(node.right, fn);
    ,
    sumOfTree = result => node => result.sum += node.value;

// call
const
    result =  sum: 0 ,
    getSum = sumOfTree(result);

traverse(tree, getSum);
console.log(result.sum);

【讨论】:

我总是从你的 js 实现中学习。

以上是关于树遍历,传递的变量在退出最终递归时重置为0(Javascript)的主要内容,如果未能解决你的问题,请参考以下文章

js 循环遍历变量的几种方式

vue递归遍历Json树状数据

二叉树遍历的非递归

树538. 把二叉搜索树转换为累加树

Django 将保存变量重置为 0,最好在主视图中

二叉树模板--剑指offer第4题