递归地返回二叉树python中节点的平均值

时间:2017-03-05 23:08:12

标签: python recursion binary-tree binary-search-tree

假设我有一个二叉树: enter image description here

我想创建一个返回树的平均值的函数,在本例中为(5+3+2+6)/(4) = 4

我是否预先遍历遍历,这是我的功能:

def helper(root, total=0, amount=0):
    if root != None:
        total += root.data
        amount += 1
        helper(root.left, total, amount)
        helper(root.right, total, amount)      

    return (root, total, amount)

def avg(root):
    av = helper(root)
    return av[1]/av[2]

但是此代码仅返回(Node 5, total = 5, amount = 1)。它只扫描first节点,而且我不知道上面代码的原因或错误。

class btn(object):

    def __init__(self, data):
        self.data = data
        self.left = None
        self.right = None

3 个答案:

答案 0 :(得分:1)

检测到反模式

您已经在使用状态变量的递归 - 不需要使用重新分配来增加更多的复杂性。这个页面上的其他答案因为犯同样的错误而失败了

def avg (tree):
  def helper (node, sum, count):
    if node is None:
      return (0, 0)
    else:
      (Lsum, Lcount) = helper(node.left, 0, 0)
      (Rsum, Rcount) = helper(node.right, 0, 0)
      return (node.data + Lsum + Rsum, 1 + Lcount + Rcount)
  (sum, count) = helper(tree, 0, 0)
  return sum/count if count > 0 else None

# your node class
class Node (object):
  def __init__(self, data, left, right):
    self.data = data
    self.left = left
    self.right = right

# make your tree
tree = Node(5, Node(3, Node(2, None, None), None), Node(6, None, None))

print(avg(tree)) #=> 4.0

# ensure that this works for an empty tree too (it does)
print(avg(None)) #=> None

<强>直觉

递归使我们能够对此产生非常好的直觉 - 特别是粗体

(Lsum, Lcount) = helper(node.left, 0, 0)
(Rsum, Rcount) = helper(node.right, 0, 0)
return (node.data + Lsum + Rsum, 1 + Lcount + Rcount)

return说要返回(sum, count)的元组

  • 对于总和,此节点的数据加上左右总和
  • 对于计数,计算此节点(1)加上左右计数

通过这种方式编写,我们可以非常清楚地看到我们的函数必须处理的两种情况:

  1. 当节点为None时,我们会为最终计算提供(0, 0)
  2. 否则,我们会将节点的data提供给总和,将1提供给计数
  3. 内联代码说明

    # we only need to expose one function
    def avg (tree):
    
      # helper isn't exposed outside of avg
      # helper has stateful parameters
      def helper (node, sum, count):
    
        # helper is a recursive function, start with the base case of empty Node
        if node is None:
          # our base sum and count are 0
          return (0, 0)
    
        # process the Node
        else:
    
          # get L sum and count the same way we initialized helper with the tree
          (Lsum, Lcount) = helper(node.left, 0, 0)
    
          # do the same for the R side
          (Rsum, Rcount) = helper(node.right, 0, 0)
    
          # no reassignment of sum or count is necessary,
          # simply recurse using the new state values of each
          return (
            node.data + Lsum + Rsum, # sum equals this node plus L and R sums
            1 + Lcount + Rcount      # count equals 1 plus L and R counts
          )
    
      # always init the sum and count with 0
      (sum, count) = helper(tree, 0, 0)
    
      # don't divide by zero if the tree is empty; instead return None
      return sum/count if count > 0 else None
    

    清理所有中间值

    让我们来看看这个

    (Lsum, Lcount) = helper(node.left, 0, 0)
    (Rsum, Rcount) = helper(node.right, 0, 0)
    return (node.data + Lsum + Rsum, 1 + Lcount + Rcount)
    

    如果你像我一样,尽管这是使用重新分配对其他答案的巨大改进,但它仍然使用 4 中间值。如果我们能够清理一下这一点,那就太好了 - 我们可以。

    如果我们有一个可以获取元组列表的函数,并在各自的位置添加所有值,该怎么办?

    // if only ...
    sum_tuples( (0, 10), (1, 20), (2, 30) )
    # ... ( 0 + 1 + 2 , 10 + 20 + 30 )
    #=> (3, 60)
    

    事实证明,在zip的帮助下,该函数实际上很容易编写。这个函数是通用的,所以它在我们的avg函数以外的地方很有用,所以我将单独定义它

    def sum_tuples (*xs):
      return tuple(sum(x) for x in zip(*xs))
    
    sum_tuples( (0,10), (1,20), (2,30) )
    #=> (3, 60)
    

    现在看看它对avg的影响 - 不再有中间值(粗体的变化)

    def avg (tree):
      def helper (node, sum, count):
        if node is None:
          return (0, 0)
        else:
          return sum_tuples(
            (node.data, 1),
            helper(node.left, 0, 0),
            helper(node.right, 0, 0)
          )
      (sum, count) = helper(tree, 0, 0)
      return sum/count if count > 0 else None

    当然它的工作原理和以前一样,只是现在才变得如此美丽。

答案 1 :(得分:-1)

调用helper时,您没有使用返回值。这些应该用于确定整个树中的值的总和:

def helper(root, total=0, amount=0):
    if root != None:
        total += root.data
        amount += 1
        _, left_total, left_amount = helper(root.left, total, amount)
        _, right_total, right_amount = helper(root.right, total, amount)
        total += left_total
        total += right_total
        amount += left_amount
        amount += right_amount

    return (root, total, amount)

以下行将helper的返回值“解包”为三个值中的每一个:

_, left_total, left_amount = helper(root.left, total, amount)

这三个值中的第一个值已分配给_,但被忽略(在这种情况下通常使用变量_)然后有left_total和{{1}这是返回的元组中的第二个和第三个值。

答案 2 :(得分:-1)

def helper(root, total=0, amount=0):
if root != None:
    total += root.data
    amount += 1
    (_, total, amount) = helper(root.left, total, amount)
    (_, total, amount) = helper(root.right, total, amount)
return (root, total, amount)

你给出了帮助函数的当前总数和金额,但是你没有存储它们返回的新值。