汇总子值并保存在中间步骤中计算的值

时间:2011-02-15 01:48:19

标签: binary-tree

struct node { 
    int value; 
    struct node* left; 
    struct node* right; 
    int left_sum;
    int right_sum;
} 

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

通过在节点定义中添加struct node* parent链接来自下而上更容易吗?

1 个答案:

答案 0 :(得分:8)

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

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

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

递归的本质是根据具有终止条件的类似,更简单的操作来定义操作。

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

检查以下伪代码:

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)的值加上左右子树的总和,它的左子树是空的,它的右子树是F(1)。

因为你是递归地执行此操作,所以显式不会向上树。这是事实,递归调用返回的是具有该能力的求和值。换句话说,它发生在幕后。


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

  

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

您实际上从未重复使用给定子树的总和。在求和计算过程中,您只需计算B-and-below子树一次,作为将其添加到AC-and-below子树的一部分。

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

现在 某些可能有用的情况。例如,如果树本身很少变化,但是你非常想要总和,那么在更新时这样做是明智的,这样成本就会在很多读取中分摊。

我有时会将这种方法用于数据库(大多数情况下读取的次数比写入的频率高得多),但在“普通”二叉树中看到它是不常见的。

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

删除节点时,从总和中减去其值。这为您提供了非常快速的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)

只会在第一次调用时产生费用。其他四个应该是快速的,因为总和是缓存的。