产生二叉树的所有从根到叶的分支

时间:2019-03-29 19:36:34

标签: python python-3.x data-structures binary-tree

很抱歉,如果这是一个常见问题,但是我没有找到适合我特定问题的适当答案。我正在尝试实现一种walk方法,该方法将二叉树从其根节点移动到其每个叶节点,并在到达叶节点时生成从根到叶的路径。例如,遍历由以下表示的二叉树:

     __a__
    /     \
   b       d
  / \     / \
 -   c   -   -

会产生:

['a', 'b', 'c']
['a', 'd']

我的想法是BinaryTree.walk在根节点上调用Node.traverse,而根节点又递归地调用每个子节点的traverse方法。 BinaryTree.walk还会创建一个空列表,每次调用traverse时都会传递一个空列表,附加每个节点的数据,一旦到达叶节点,就产生该列表,并在访问每个元素后将每个元素从列表中弹出节点。

在某些时候,尽管出了点问题。这是我的代码:

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

    def __repr__(self):
        return f"{self.__class__.__name__}({self.data})"

    @property
    def children(self):
        return self.left, self.right

    def traverse(self, branch):
        print('ON NODE:', self)
        branch.append(self.data)
        if self.left is None and self.right is None:
            yield branch
        else:
            for child in self.children:
                if child is not None:
                    print('ENTERING CHILD:', child)
                    child.traverse(branch=branch)
                    print('EXITING CHILD:', child)
                    branch.pop()


class BinaryTree:
    def __init__(self, root=Node()):
        if not isinstance(root, Node):
            raise ValueError(f"Tree root must be Node, not {type(root)}")
        self.root = root

    def __repr__(self):
        return f"{self.__class__.__name__}({self.root})"

    def walk(self):
        node = self.root
        branch = []
        yield from node.traverse(branch=branch)


if __name__ == '__main__':
    # create root node
    n0 = Node('A')
    # create binary tree with root node
    tree = BinaryTree(root=n0)
    # create others nodes
    n1 = Node(data='B')
    n2 = Node(data='C')
    n3 = Node(data='D')
    # connect nodes
    n0.left = n1
    n0.right = n3
    n1.right = n2

    # walk tree and yield branches
    for branch in tree.walk():
        print(branch)

预期输出:

ON NODE: Node(A)
ENTERING CHILD: Node(B)
ON NODE: Node(B)
ENTERING CHILD: Node(C)
ON NODE: Node(C)
['A', 'B', 'C']  # yielded branch
EXITING CHILD: Node(C)
EXITING CHILD: Node(B)
ENTERING CHILD: Node(D)
ON NODE: Node(D)
['A', 'D']  # yielded branch
EXITING CHILD: Node(D)

实际输出:

ON NODE: Node(A)
ENTERING CHILD: Node(B)
EXITING CHILD: Node(B)
ENTERING CHILD: Node(D)
EXITING CHILD: Node(D)
IndexError: pop from empty list

我知道我对列表做错了,因为它在空时尝试弹出,但是我不明白它是如何到达的。对于每个pop调用,它应该调用一次append

我也不知道为什么要输入和退出节点,但是没有显示ON NODE:消息...就像我的代码以某种方式跳过了child.traverse(branch=branch)行一样?< / p>

有人可以帮助我了解我在哪里搞砸吗?

在此先感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

一个很好的答案here

复制其Python示例:

""" 
Python program to print all path from root to 
leaf in a binary tree 
"""

# binary tree node contains data field ,  
# left and right pointer 
class Node: 
    # constructor to create tree node 
    def __init__(self, data): 
        self.data = data 
        self.left = None
        self.right = None

# function to print all path from root 
# to leaf in binary tree 
def printPaths(root): 
    # list to store path 
    path = [] 
    printPathsRec(root, path, 0) 

# Helper function to print path from root  
# to leaf in binary tree 
def printPathsRec(root, path, pathLen): 

    # Base condition - if binary tree is 
    # empty return 
    if root is None: 
        return

    # add current root's data into  
    # path_ar list 

    # if length of list is gre 
    if(len(path) > pathLen):  
        path[pathLen] = root.data 
    else: 
        path.append(root.data) 

    # increment pathLen by 1 
    pathLen = pathLen + 1

    if root.left is None and root.right is None: 

        # leaf node then print the list 
        printArray(path, pathLen) 
    else: 
        # try for left and right subtree 
        printPathsRec(root.left, path, pathLen) 
        printPathsRec(root.right, path, pathLen) 

# Helper function to print list in which  
# root-to-leaf path is stored 
def printArray(ints, len): 
    for i in ints[0 : len]: 
        print(i," ",end="") 
    print() 

# Driver program to test above function 
""" 
Constructed binary tree is  
      10 
    /   \ 
   8     2 
  / \   / 
 3   5 2 
"""
root = Node(10) 
root.left = Node(8) 
root.right = Node(2) 
root.left.left = Node(3) 
root.left.right = Node(5) 
root.right.left = Node(2) 
printPaths(root) 

# This code has been contributed by Shweta Singh. 

赠予:

  

10 8 3
  10 8 5
  10 2 2

您也可以给它字母:

root = Node("A") 
root.left = Node("B") 
root.right = Node("D") 
root.left.right = Node("C") 
printPaths(root) 

赠予:

  

A B C
  D

答案 1 :(得分:1)

这是您代码的变体。

code.py

#!/usr/bin/env python3

import sys


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

    def __repr__(self):
        return f"{self.__class__.__name__}({self.data})"

    @property
    def children(self):
        if self.left:
            yield self.left
        if self.right:
            yield self.right

    @property
    def is_leaf(self):
        return self.left is None and self.right is None

    def traverse_preord(self, accumulator=list()):
        print("  On node:", self)
        accumulator.append(self.data)
        if self.is_leaf:
            yield accumulator
        else:
            for child in self.children:
                print("  Entering child:", child)
                yield from child.traverse_preord(accumulator=accumulator)
                accumulator.pop()
                print("  Exiting child:", child)


def main():
    root = Node(data="A",
                left=Node(data="B",
                          right=Node(data="C")
                         ),
                right=Node(data="D",
                           #left=Node(data="E"),
                           #right=Node(data="F"),
                          )
               )
    for path in root.traverse_preord():
        print("Found path:", path)


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

注释

  • 我对代码进行了一些重构(简化,更改了一些标识符名称,文本和其他无关紧要的更改)
  • 孩子属性:
      节点的 left right 属性的
    • None 表示该节点没有子节点,因此在返回结果中没有任何子节点
    • 由于问题涉及 yield ,因此我将其变成了生成器(而不是返回元组或列表,...)。结果,我必须添加 is_leaf ,因为生成器不会将其评估为 False (即使为空)

输出

[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q055424449]> "e:\Work\Dev\VEnvs\py_064_03.07.03_test0\Scripts\python.exe" code.py
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32

  On node: Node(A)
  Entering child: Node(B)
  On node: Node(B)
  Entering child: Node(C)
  On node: Node(C)
Found path: ['A', 'B', 'C']
  Exiting child: Node(C)
  Exiting child: Node(B)
  Entering child: Node(D)
  On node: Node(D)
Found path: ['A', 'D']
  Exiting child: Node(D)


您的代码有什么问题?

这是遍历重复调用(child.traverse(branch=branch))。 它创建了一个生成器,但是由于未在任何地方使用(迭代)该函数,因此实际上并未对其进行调用,导致尝试删除的元素多于添加的元素(仅 < em> 1 :这是根节点)。
所以事实证明你快到了。您所要做的就是在其前面添加 yield from :)。
有关[Python]: PEP 380 -- Syntax for Delegating to a Subgenerator的更多详细信息。