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

Python 递归返回二叉树中节点的平均值,python,recursion,binary-tree,binary-search-tree,Python,Recursion,Binary Tree,Binary Search Tree,假设我有一棵二叉树: 我想创建一个函数,返回树的平均值,在本例中为(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, am

假设我有一棵二叉树:

我想创建一个函数,返回树的平均值,在本例中为
(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]
但是,此代码仅返回
(节点5,总计=5,金额=1)
。就像它只扫描
第一个
节点一样,我不知道上面的代码为什么有问题,或者有什么问题

class btn(object):

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

调用
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)
三个值中的第一个被分配给
\uu
,但被忽略(在这种情况下通常使用变量
\u
),然后还有
left\u total
left\u amount
,它们是返回元组中的第二个和第三个值

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)

您正在将当前总数和金额提供给帮助函数,但没有存储它们返回的新值。

检测到的反模式

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
您已经对状态变量使用了递归——不需要使用重新分配来增加复杂性。这一页上的其他答案也犯了同样的错误,让你失望

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

直觉

# 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)

清除所有中间值

我们来看看这个

(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平均值(树):
def辅助对象(节点、总和、计数):
如果节点为无:
返回(0,0)
其他:
返回和元组(
(node.data,1),
辅助对象(node.left,0,0),
辅助对象(node.right,0,0)
)
(总和,计数)=辅助对象(树,0,0)
如果计数>0,则返回总和/计数,否则无

当然,它的工作原理和以前一样,只是现在它已经尽可能的漂亮了。

这个
是什么意思?我明白你的意思,但是请解释一下上面的语法?@VeeshaDawg我已经扩展了答案。你为什么不把它们存储为
root
?@shadveawg这个值没有被使用。我们甚至可以更新代码,不将
root
节点作为第一个值返回(除非有理由将该代码保留在那里)。