【问题标题】:Sum up child values and save the values calculated in intermediate steps汇总子值并保存中间步骤计算的值
【发布时间】:2011-02-15 01:48:18
【问题描述】:
struct node { 
    int value; 
    struct node* left; 
    struct node* right; 
    int left_sum;
    int right_sum;
} 

在二叉树中,从特定节点开始,有一个简单的递归算法来总结其所有子值。有没有办法保存在中间步骤中计算的值并将它们作为left_sumright_sum 存储在子节点中?

struct node* parent 链接添加到节点定义会更容易做到这一点吗?

【问题讨论】:

    标签: binary-tree


    【解决方案1】:

    不,这显然是一个递归练习。想想总和是什么意思。它是零加上“从根向下的所有值的总和”。

    有趣的是,“从根节点向下的所有值的总和”是根节点的值加上“从它的左节点向下的所有值的总和”加上 em>“从其右节点向下的所有值的总和”。

    希望你能看到我要去的地方。

    递归的本质是用类似的、更简单的、带有终止条件的操作来定义一个操作。

    在这种情况下,终止条件是树的叶节点,或者为了简化代码,超出叶节点。

    检查以下伪代码:

    def sumAllNodes (node):
        if node == NULL:
            return 0
        return node.value + sumAllNodes (node.left) + sumAllNodes (node.right)
    
    fullSum = sumAllNodes (rootnode)
    

    这就是它的全部内容。使用以下树:

               __A(9)__
              /        \
          B(3)          C(2)
         /    \             \
    D(21)      E(7)          F(1)
    

    使用伪代码,总和是A (9) 的值加上左右子树的总和。

    A 的左子树是B (3) 的值加上 左右子树的总和。

    B 的左子树是D 的值 (21) 加上 左右子树的总和。

    D 的左子树是NULL (0) 的值。

    稍后,A 的右子树是C 的值 (2) 加上 its 左右子树的总和,它的左子树为空,其右子树为F(1).

    因为您是递归地执行此操作,所以您永远不会明确沿着树的方向向上。事实上,递归调用返回的总和值赋予了这种能力。换句话说,它发生在幕后。


    虽然您问题的另一部分并不是真正有用,但当然,可能存在我没有考虑到的未说明的要求,因为它们......未说明:-)

    有没有办法将中间步骤中计算出来的值保存起来,作为left_sum和right_sum存储在子节点中?

    您永远不会真正重复使用给定子树的总和。在求和计算期间,您将只计算B-and-below 子树一次,作为将其添加到AC-and-below 子树的一部分。

    可以存储这些值,以便 B 包含该值和两个总和(左和右) - 这意味着对树的每次更改都必须传播到root 也是如此,但它是可行的。

    现在一些可能有用的情况。例如,如果树本身很少更改,但您需要非常频繁地求总和,则在更新时执行此操作是明智的性能明智之举,以便在大量读取中分摊成本。

    我有时会将此方法用于数据库(大多数情况下,数据库的读取频率远高于写入频率),但在“普通”二叉树中很少见。

    另一个可能的优化:只需将总和作为树对象中的一个单独变量进行维护。将其初始化为零,然后,无论何时添加一个节点,都将其值添加到总和中。

    当你删除一个节点时,从总和中减去它的值。这为您提供了非常快速的 O(1)“返回总和”函数,而无需在更新时向上传播。

    缺点是您只有整个树的总和,但我很难想出一个需要子树总和的有效用例。如果您这样的用例,那么我会选择类似的东西:

    def updateAllNodes (node):
        if node == NULL:
            return 0
        node.leftSum = updateAllNodes (node.left)
        node.rightSum = updateAllNodes (node.right)
        return node.value + node.leftSum + node.rightSum
    
    change the tree somehow (possibly many times)
    fullSum = updateAllNodes (root)
    

    换句话说,只需在每次更改后更新整个树(或批量更改然后如果您知道发生了很多更改,则更新)。这可能比尝试将其作为树更新本身的一部分来简单一些。

    您甚至可以使用单独的dirtyFlag,每当树发生变化时将其设置为 true,并在您计算和存储总和时设置为 false。然后在总和计算代码中使用它,仅在脏的情况下进行重新计算(换句话说,总和的缓存)。

    这样,代码如下:

    fullSum = updateAllNodes (root)
    fullSum = updateAllNodes (root)
    fullSum = updateAllNodes (root)
    fullSum = updateAllNodes (root)
    fullSum = updateAllNodes (root)
    

    只会在第一次调用时产生费用。由于总和被缓存,其他四个应该快得令人眼花缭乱。

    【讨论】:

    • 谢谢,我知道你给出的算法。但是,在上图中,我想要 B.left_sum = 21, B.right_sum = 7; C.right_sum = 1; A.left_sum = 21+7+3 = 31, A.right_sum = 2+1 = 3。我想要一种方法将这些中间值存储在每个节点中,而不仅仅是最终结果。
    • 谢谢,这就是我要找的。​​span>
    猜你喜欢
    • 2013-02-25
    • 2023-03-13
    • 1970-01-01
    • 1970-01-01
    • 2014-11-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多