检测到反模式
您已经在对状态变量使用递归 - 无需使用重新分配来增加复杂性。此页面上的其他答案因犯了同样的错误而使您失望
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) 的元组
- 对于 sum,此节点的数据加上左右和的任何值
- 对于 count,计算这个节点 (1) 加上左右计数是什么
这样写,我们可以很清楚地看到我们的函数必须处理的两种情况:
- 当节点为
None时,我们将(0, 0)贡献给最终计算
- 否则,我们将节点的
data 贡献给sum,并将1 贡献给count。
内联代码说明
# 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 的影响——不再有中间值(bold 的变化)
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
当然它的工作原理和以前一样,只是现在它已经尽可能漂亮了。