[英]Given a binary tree and a number ‘S’, find all paths from root-to-leaf such that the sum of all the node values of each path equals ‘S’
The example below is from a source online and what I'm not sure of is why we need to append to allPaths
a new copy of currentPath
.下面的示例来自在线来源,我不确定为什么我们需要将
currentPath
的新副本附加到allPaths
。 I thought that in deleting the last element as we go back up the recursive call stack by doing del currentPath[-1]
makes sure that we are not adding the previous path to the new path我认为在通过执行
del currentPath[-1]
返回递归调用堆栈时删除最后一个元素可确保我们不会将先前的路径添加到新路径
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def find_paths(root, required_sum):
allPaths = []
find_paths_recursive(root, required_sum, [], allPaths)
return allPaths
def find_paths_recursive(currentNode, required_sum, currentPath, allPaths):
if currentNode is None:
return
# add the current node to the path
currentPath.append(currentNode.val)
# if the current node is a leaf and its value is equal to required_sum, save the current path
if currentNode.val == required_sum and currentNode.left is None and currentNode.right is None:
allPaths.append(list(currentPath))
else:
# traverse the left sub-tree
find_paths_recursive(currentNode.left, required_sum -
currentNode.val, currentPath, allPaths)
# traverse the right sub-tree
find_paths_recursive(currentNode.right, required_sum -
currentNode.val, currentPath, allPaths)
# remove the current node from the path to backtrack,
# we need to remove the current node while we are going up the recursive call stack.
del currentPath[-1]
def main():
root = TreeNode(12)
root.left = TreeNode(7)
root.right = TreeNode(1)
root.left.left = TreeNode(4)
root.right.left = TreeNode(10)
root.right.right = TreeNode(5)
required_sum = 23
print("Tree paths with required_sum " + str(required_sum) +
": " + str(find_paths(root, required_sum)))
main()
It is important to realise that during the whole process there is only one currentPath
list.需要注意的是,在整个过程中只有一个
currentPath
列表。 It is the one that is created in the initial call:它是在初始调用中创建的:
find_paths_recursive(root, required_sum, [], allPaths)
# ^^---here!
All that happens to that single list is that elements are appended to it, and then deleted from it again (when backtracking).发生在该单个列表上的所有事情都是将元素附加到它上面,然后再次从其中删除(回溯时)。 It continually grows and shrinks, grows and shrinks,... throughout its lifetime.
它在它的一生中不断地增长和缩小,增长和缩小,......。 But it is the same, single list instance.
但它是相同的单列表实例。
If you were to append that list to allPaths
without taking a copy, ie like this:如果您将该列表附加到
allPaths
而不复制,即像这样:
allPaths.append(currentPath)
...then realise that while that list is a member of allPaths
, it will be mutated by future append
and delete
actions on it! ...然后意识到虽然该列表是
allPaths
的成员,但它会被未来的append
和delete
操作改变! Even more: as the above statement is executed again later on:甚至更多:因为上面的语句稍后再次执行:
allPaths.append(currentPath)
... exactly the same list is appended that is already in allPaths
... because there is only one currentPath
list! ...完全相同的列表被追加,它已经在
allPaths
......因为只有一个currentPath
列表! So you'll end up with allPaths
having repeated references to one and the same list.所以你最终会得到
allPaths
重复引用同一个列表。
Concluding: it is important to take a copy of currentPath
which will not be mutated any more by the future mutations on currentPath
.结论:重要的是要采取的副本
currentPath
,不会通过对未来突变的突变更多currentPath
。 It is like taking a snapshot of the current state of currentPath
.这就像对
currentPath
的当前状态进行快照。
The find_paths_recursive
function was designed such that appending to allPaths
is the way to return the results to the caller. find_paths_recursive
函数被设计成附加到allPaths
是将结果返回给调用者的方式。
def find_paths(root, required_sum):
allPaths = []
find_paths_recursive(root, required_sum, [], allPaths)
return allPaths
Here in find_paths
, allPaths
is passed to find_paths_recursive
as an empty list and after it is done, it will contain the results (paths from root to leaf which fulfill the described condition).在
find_paths
, allPaths
作为空列表传递给find_paths_recursive
,完成后,它将包含结果(从根到叶的路径满足所描述的条件)。
I would recommend breaking the problem down into separate parts.我建议将问题分解为单独的部分。 First we write a generic
paths
function -首先我们写一个通用的
paths
函数——
def paths (t = None, p = ()):
if not t:
return
elif t.left or t.right:
yield from paths(t.left, (*p, t.val))
yield from paths(t.right, (*p, t.val))
else:
yield (*p, t.val)
mytree = TreeNode \
( 12
, TreeNode(7, TreeNode(4))
, TreeNode(1, TreeNode(10))
)
Now we can see how paths
works -现在我们可以看到
paths
是如何工作的 -
for p in paths(mytree):
print(p)
(12, 7, 4)
(12, 1, 10)
Now we can write solver
that specializes paths
-现在我们可以编写专门处理
paths
solver
-
def solver (t = None, q = 0):
for p in paths(t):
if sum(p) == q:
yield p
solver
is a generator which yields all possible solutions. solver
是生成所有可能解决方案的生成器。 It is a good choice for programs like this because you can pause/cancel the solving as soon as you found the solution(s) you are looking for -对于此类程序来说,这是一个不错的选择,因为您可以在找到所需的解决方案后立即暂停/取消求解 -
for sln in solver(mytree, 23):
print(sln)
The output isn't particularly interesting because each branch in mytree
sums to 23 -输出并不是特别有趣,因为
mytree
每个分支总和为 23 -
(12, 7, 4)
(12, 1, 10)
If we make anothertree
with different values, we can see more interesting output -如果我们用不同的值制作另一个
anothertree
,我们可以看到更有趣的输出 -
anothertree = TreeNode \
( 1
, TreeNode(7, TreeNode(4), TreeNode(5))
, TreeNode(9, TreeNode(2), TreeNode(7))
)
for sln in solver(anothertree, 12):
print(sln)
(1, 7, 4)
(1, 9, 2)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.