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

Posted

技术标签:

【中文标题】使用 DFS 检查二叉树中是不是存在路径不起作用【英文标题】:Checking If a path exists in a Binary Tree using DFS not working使用 DFS 检查二叉树中是否存在路径不起作用 【发布时间】:2021-09-29 03:01:41 【问题描述】:

我需要检查二叉树中是否存在路径。我得到了一个元素列表和一个要遍历的二叉树。我正在使用 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, 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, sequence_index + 1) or \
         find_path_helper(current_node.right, sequence, sequence_index + 1

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

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, [])

def find_path_helper(current_node, sequence, 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, sequence, current_sequence) or \
         find_path_helper(current_node.right, sequence, current_sequence)

但即使为 true,也只会返回 false。我在想 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, 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, [])

def find_path_helper(current_node, sequence, 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, sequence, list(current_sequence)) or \
         find_path_helper(current_node.right, sequence, list(current_sequence))

最后这段代码有效,但我不知道为什么我必须传递current_sequence 的副本

【问题讨论】:

你如何代表树? 发布的代码不是有效的 Python。 抱歉忘记添加def。它是一棵二叉树。一个父节点不超过2个子节点 请提供调用find_path的例子。 TreeNode的定义是……? 【参考方案1】:

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

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

>>> 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 节点和i-th 列表索引:

def find_path(root, sequence, depth=0):
    if (
        root and 
        not root.left and 
        not root.right and
        depth == len(sequence) - 1 and 
        root.data == sequence[depth]
    ):
        return True
    elif (
        not root or 
        depth >= len(sequence) or 
        sequence[depth] != root.data
    ):
        return False

    return (find_path(root.left, sequence, depth + 1) or 
            find_path(root.right, sequence, depth + 1))

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


if __name__ == "__main__":
    r"""
        1
       / \
      2   3
     /   / \
    4   5   6
    """
    root = Node(
        1,
        Node(
            2,
            Node(4),
        ),
        Node(
            3,
            Node(5),
            Node(6),
        ),
    )
    should_be_true = [[1, 3, 5], [1, 3, 6], [1, 2, 4]]
    assert all(find_path(root, x) for x in should_be_true)

    should_be_false = [
        [1, 2, 5],
        [1, 3, 4],
        [1, 3],
        [1, 3, 3, 3],
        [1, 3, 5, 5],
        [],
        [1, 2],
    ]
    assert all(not find_path(root, x) for x in should_be_false)

【讨论】:

以上是关于使用 DFS 检查二叉树中是不是存在路径不起作用的主要内容,如果未能解决你的问题,请参考以下文章

使用递归 DFS 在二叉树中查找节点

力扣-124-二叉树中的最大路径和

十五:查找二叉树中所有路径

112. 路径总和. DFS

LeetCode124二叉树中的最大路径和

leetcode——124. 二叉树中的最大路径和