使用 DFS 检查二叉树中是否存在路径不起作用

问题描述

我需要检查二叉树中是否存在路径。我得到了一个元素列表和一个要遍历的二叉树。我正在使用 DFS 来检查路径是否存在。这个想法是我应该找到从根节点到叶节点的路径是否存在。

我知道如果我执行以下操作,我可以找到路径是否存在...

class TreeNode:
  def __init__(self,val,left=None,right=None):
    self.val = val
    self.left = left
    self.right = right

def find_path(root,sequence):
  if root is None:
    return len(sequence) == 0

  return find_path_helper(root,sequence,0)

def find_path_helper(current_node,sequence_index):
  if current_node is None:
    return False

  sequence_length = len(sequence)

  if sequence_index >= sequence_length or current_node.val != sequence[sequence_index]:
    return False
  
  if current_node.left is None and current_node.right is None and sequence_index == sequence_length - 1:
    return True

  return find_path_helper(current_node.left,sequence_index + 1) or \
         find_path_helper(current_node.right,sequence_index + 1

然而,我不考虑代码效率的第一个想法是保留一个从根到叶的路径列表并将其与要找到的列表进行比较,但我无法让它工作,我不确定是什么我做错了。

class TreeNode:
  def __init__(self,[])

def find_path_helper(current_node,current_sequence):
  if current_node is None:
    return False

  current_sequence.append(current_node.val)

  if current_node.left is None and current_node.right is None:
    return current_sequence == sequence

  return find_path_helper(current_node.left,current_sequence) or \
         find_path_helper(current_node.right,current_sequence)

但即使为真也只返回假。我在想 current_sequence 在从根遍历到叶子后将具有当前路径。

以树为例

         1
       /  \
      0   1
      |   |\
      1   6 5

我需要检查路径 1 -> 1 -> 6 是否存在

下面是树的例子

root = TreeNode(1)
root.left = TreeNode(0)
root.right = TreeNode(1)
root.left.left = TreeNode(1)
root.right.left = TreeNode(6)
root.right.right = TreeNode(5)

sequence = [1,1,6]

print(find_path(root,sequence))
class TreeNode:
  def __init__(self,current_sequence):
  if current_node is None:
    return False

  current_sequence.append(current_node.val)

  if current_node.left is None and current_node.right is None:
    return current_sequence == sequence

# if I pass in a copy of current_sequence it works
  # I'm not sure why this does though
  return find_path_helper(current_node.left,list(current_sequence)) or \
         find_path_helper(current_node.right,list(current_sequence))

最后一个代码有效,但我不知道为什么我必须传入 current_sequence

的副本

解决方法

当您将列表等可变对象传递给函数时,分配给它的局部变量参数不是列表中数据的副本。两个变量都引用同一个底层列表对象。通过一个变量对列表所做的更改反映在另一个变量可见的列表上。

您可以通过一个简单的示例来查看此行为:

>>> def foo(L):
...     print(id(L))
...     L[0] = 42
... 
>>> X = [1,2]
>>> id(X)
139874008311168
>>> foo(X)
139874008311168
>>> X
[42,2]

在您的递归调用中,所有本地 current_sequence 变量都引用内存中的同一个列表。

当您的递归调用解析并且调用堆栈从未找到有效路径的左子树探索中弹出回父调用范围时,current_sequence 列表仍保留来自该失败遍历的错误值。在探索正确的子树以使算法工作时,必须忽略这些值。

制作列表的副本通过使每个列表每帧成为不同的对象来解决问题,但效率低下。更好的是使用 append()pop() 来改变每次调用的同一个列表,或者完全跳过二级列表并传递一个索引/深度参数来比较 i-th 节点和 {第 {1}} 个列表索引:

i