当前位置: 首页 > 知识库问答 >
问题:

删除最右边节点时的DFS递归问题

井旺
2023-03-14

我现在正在研究DFS方法来计算路径的总和。问题陈述是:

给定一棵二叉树和一个数字“S”,找到树中的所有路径,使每条路径的所有节点值之和等于“S”。请注意,路径可以在任何节点开始或结束,但所有路径必须遵循从父节点到子节点(从上到下)的方向。

我的做法是:

def all_sum_path(root, target):
    global count
    count = 0
    find_sum_path(root, target, [])
    return count

def find_sum_path(root, target, allPath):
    global count
    if not root:
        return 0
    # add a space for current node
    allPath.append(0)
    # add current node values to all path
    allPath = [i+root.value for i in allPath]
    print(allPath)
    # check if current path == target
    for j in allPath:
        if j == target:
            count += 1
    # recursive
    find_sum_path(root.left, target, allPath)
    find_sum_path(root.right, target, allPath)
    # remove the current path
    print('after', allPath)
    allPath.pop()
    print('after pop', allPath)

class TreeNode():
    def __init__(self, _value):
        self.value = _value
        self.left, self.right, self.next = None, None, None

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)

    print(all_sum_path(root, 11))

main()

带返回:

[1]
[8, 7]
[14, 13, 6]
after [14, 13, 6]
after pop [14, 13]
[13, 12, 5, 5]
after [13, 12, 5, 5]
after pop [13, 12, 5]
after [8, 7, 0, 0]
after pop [8, 7, 0]
[10, 9, 9]
[12, 11, 11, 2]
after [12, 11, 11, 2]
after pop [12, 11, 11]
[13, 12, 12, 3, 3]
after [13, 12, 12, 3, 3]
after pop [13, 12, 12, 3]
after [10, 9, 9, 0, 0]
after pop [10, 9, 9, 0]
after [1, 0, 0]
after pop [1, 0]
4

我认为问题在于我没有成功删除列表中最右边的节点。然后我更新了我的代码如下,删除了allPath最右边的节点,并创建了一个名为newAllPath的新列表来记录已经加上当前节点值的节点。

def all_sum_path(root, target):
    global count
    count = 0
    find_sum_path(root, target, [])
    return count

def find_sum_path(root, target, allPath):
    global count
    if not root:
        return 0
    # add a space for current node
    allPath.append(0)
    # add current node values to all path
    newAllPath = [i+root.value for i in allPath]
    print(allPath, newAllPath)
    # check if current path == target
    for j in newAllPath:
        if j == target:
            count += 1
    # recursive
    find_sum_path(root.left, target, newAllPath)
    find_sum_path(root.right, target, newAllPath)
    # remove the current path
    print('after', allPath, newAllPath)
    allPath.pop()
    print('after pop', allPath, newAllPath)

class TreeNode():
    def __init__(self, _value):
        self.value = _value
        self.left, self.right, self.next = None, None, None

def main():
    root = TreeNode(1)
    root.left = TreeNode(7)
    root.right = TreeNode(9)
    root.left.left = TreeNode(6)
    root.left.right = TreeNode(5)
    root.right.left = TreeNode(2)
    root.right.right = TreeNode(3)

    print(all_sum_path(root, 12))

    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)

    print(all_sum_path(root, 11))

main()

带返回:

[0] [1]
[1, 0] [8, 7]
[8, 7, 0] [14, 13, 6]
after [8, 7, 0] [14, 13, 6]
after pop [8, 7] [14, 13, 6]
[8, 7, 0] [13, 12, 5]
after [8, 7, 0] [13, 12, 5]
after pop [8, 7] [13, 12, 5]
after [1, 0] [8, 7]
after pop [1] [8, 7]
[1, 0] [10, 9]
[10, 9, 0] [12, 11, 2]
after [10, 9, 0] [12, 11, 2]
after pop [10, 9] [12, 11, 2]
[10, 9, 0] [13, 12, 3]
after [10, 9, 0] [13, 12, 3]
after pop [10, 9] [13, 12, 3]
after [1, 0] [10, 9]
after pop [1] [10, 9]
after [0] [1]
after pop [] [1]
3

我不确定为什么在第一种方法中无法成功删除最右边的节点。然而,在我的第二种方法中,一旦我删除了allPath中最右边的节点,它也会删除newAllPath中的节点。

谢谢你的帮助。我很困惑,一整天都被困在这里。

共有2个答案

施永贞
2023-03-14

功能原理

这是一个非常重要的问题,我不会尝试调试您的程序,因为它违背了递归的使用方式。重申一下,递归是一种函数遗产,因此将其与函数风格结合使用会产生最好的结果。这意味着避免-

  • 突变,如. append=1. pop
  • 重新分配像左=...右=...allPath=...
  • 全局像计数
  • 其他副作用,如print

分解

试图将任务的所有关注点打包到一个函数中是个坏主意。把问题分解成不同的部分有很多好处-

  • 更小的函数更容易读、写、测试和调试
  • 单一用途的功能更易于重用

首先,我们将使用我们在之前的Q中已经写过的find_sum

def find_sum(t, q, path = []):
  if not t:
    return
  elif t.value == q:
    yield [*path, t.value]
  else:
    yield from find_sum(t.left, q - t.value, [*path, t.value])
    yield from find_sum(t.right, q - t.value, [*path, t.value])

使用find_sum我们可以轻松编写all_sum-

def all_sum(t, q):
  for n in traverse(t):
    yield from find_sum(n, q)

这需要我们编写一个通用的遍历-

def traverse(t):
  if not t:
    return
  else:
    yield from traverse(t.left)
    yield t
    yield from traverse(t.right)

让我们看看它在样本树上的工作情况-

               12
             /    \
            /      \
           7        1
          / \      / \
         4   3    10  5
            /    /
           1    1

我们使用您的TreeNode构造函数来表示它-

t1 = TreeNode \
  ( 12
  , TreeNode(7, TreeNode(4), TreeNode(3, TreeNode(1)))
  , TreeNode(1, TreeNode(10, TreeNode(1)), TreeNode(5))
  )

print(list(all_sum(t1, 11)))
[[7, 4], [7, 3, 1], [10, 1], [1, 10]]

计算所有的金额

如果唯一的目标是计算总和,我们可以编写count_all_sum作为all_sum-

def count_all_sum (t, q):
  return len(list(all_sum(t, q)))
子车飞鹏
2023-03-14

除非你的树深度超过1000个节点,否则你可以使用递归来获得更简单的代码:

def findSums(node,target):
    if not node : return
    if node.value == target: yield [node.value]           # target reached, return path
    for child in (node.left,node.right):                  # traverse tree DFS
        yield from findSums(child,target)                 # paths skipping this node 
        for subPath in findSums(child,target-node.value): # paths with remainder
            yield [node.value]+subPath                    # value + sub-path

for sp in findSums(root,11):
    print(sp)

# [7, 4]
# [1, 10]

要打印二叉树,请参阅以下内容:https://stackoverflow.com/a/49844237/5237560

 类似资料:
  • 我正在学习数据结构,并试图理解Java中的链接列表。我的问题是,我有麻烦与删除节点在给定的索引递归。我的目标是得到O(log n),而不是使用循环,最后得到O(n)。 因此,当我试图删除索引2的条目时,它会删除该索引之前的所有数字,但不会删除该索引-因此它会删除[0]和[1],但不会删除[2]。 例如,在此代码中,删除前的数组填充为:。调用后,它有以下条目: 我只想删除13,这样数组就会像这样:

  • 给定一个链表和一个指定的数据值,我想递归地删除包含所述数据的所有节点。(我已经找到了迭代的方法,但我想这样做)。我已将我的结构定义为: 为了删除,我做了这个助手函数,它(应该)返回指向我删除列表的头节点的指针: 然后我想在我的实际列表中使用它: 但这不起作用。看起来我的助手函数实际上不起作用,但我无法理解。出什么事了?

  • 所以我有一个链接列表,我希望能够删除一个数字的第一次出现, 我正在尝试使用递归,但不幸的是,我最终只能删除列表的头部 我有三个不同的类,一个用于末尾的空列表,另一个类声明这个方法和实际的列表。

  • 问题内容: 我目前正在尝试递归删除目录…奇怪的是,我能够找到的最短代码是以下结构,采用了一个 临时内部类 并且采用了 访问者模式 … 资料来源:这里 鉴于新的API消除了太多的混乱和样板,这让人感到非常笨拙和冗长。 有没有更短的方法可以实现强制递归目录删除? 我正在寻找纯本地Java 1.8方法,所以请不要链接到外部库… 问题答案: 您可以结合使用NIO 2和Stream API。 -返回以下所有

  • 我在深度优先搜索算法实现的递归方法方面遇到了一些麻烦。这是二叉树照片: 该方法在树的右侧(55、89、144)工作得很好,但是当它来到左侧时,它返回nil,即使它输入“是”。那么,代码有什么问题呢?节点是Node类的一个实例,它具有值(整数)并链接到左右子级(Node类的其他实例),如果它没有来自该侧的子级,则为nil。 下面是方法代码:

  • 嘿,我一直在研究BFS/DFS,我注意到它们中的许多都有一个轻微的修改,即当一个节点添加到访问集时。 在某种程度上,算法将从堆栈/队列中弹出节点,然后将其添加到访问集。然后它会添加所有没有被访问过的邻居 在另一个实现中,节点不会添加到访问集。相反,它会将所有未访问的邻居添加到堆栈/队列中,但会在将这些邻居添加到堆栈/队列时将其添加到已访问集中。 总之,在一种方法中,当它们弹出到堆栈/队列时,它们被