简体   繁体   English

给定一个二叉树和一个数字“S”,找到从根到叶的所有路径,使得每条路径的所有节点值的总和等于“S”

[英]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的成员,但它被未来的appenddelete操作改变! 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_pathsallPaths作为空列表传递给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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM