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

如何在Python中将二叉树打印为节点结构

鞠征
2023-03-14

我有一个python代码来将字符串数学表达式转换为二叉树并对树的节点进行排序,这样左边的孩子总是比右边的孩子小。我想按以下顺序打印二叉树。

例如,考虑数学表达式((2*75)/4)。buildParseTree()将字符串表达式转换为树并printNodeInLevels()重新排列节点,以便在每个级别上左子节点小于右子节点。操作

  +
  /\
 4  *
    /\
   2  75

我想按如下方式打印它。我该怎么办?因为数学表达式的长度一直在变化,例如(24*2),((5-1)*(2/3)),(20-(5-4))等

Node("+") #root
    .addkid(Node("*") #right child at level 1
        .addkid(Node("75")) #right child at level 2
        .addkid(Node("2")) #left child at level 2
            )
    .addkid(Node("4")) #left child at level 1

我已经研究出了按顺序遍历模式按级别打印节点的方法。如果我按如下方式调用该方法,它将打印以下内容:

pt = buildParseTree("( ( 2 * 74 ) / 4 )")

printNodesInLevels(pt)

输出:

/ 
4 * 
2 74 

共有3个答案

暴博远
2023-03-14

首先,您应该阅读python的PEP8代码约定,因为它说函数、属性和变量应该在snake_case中。

您以迭代方式打印,因此这意味着您无法以等腰三角形打印,因为您无法知道基部(树的最低部分)的大小,以迭代方式,您应该将其打印为具有90度角的三角形。

或者你可以把所有的信息收集到一个列表或一个字符串和格式中,然后打印出来。想想头部,然后是中间有线的孩子。

龙晟睿
2023-03-14

一个简单而粗糙的:

from collections import deque
def print_tree(root):
    res = []
    q = deque([root])
    while q:
        row = []
        for _ in range(len(q)):
            node = q.popleft()
            if not node:
                row.append("#")
                continue
            row.append(node.val)
            q.append(node.left)
            q.append(node.right)
        res.append(row)
    rows = len(res)
    base = 2**(rows)
    for r in range(rows):
        for v in res[r]:
            print("." * (base), end = "")
            print(v, end = "")
            print("." * (base - 1), end = "")
        print("|")
        base //= 2

print_tree(root)
汪信鸥
2023-03-14

这是我创建的一个函数,用于打印任何二叉树结构。

它非常通用,只需要一个起始节点(根)和一个函数(或lambda)来获取标签和左/右子节点:

您通常会在Node类上这样使用它:

printBTree(rootNode,lambda n: (n.operand, n.left, n.right) )

# assuming the Node class has a string property named operand
# and left,right properties that return a Node or None

二次方程(-b/-sqrt(b**2-4*A*c))/(2*A)可以这样打印:

#         /
#     ___/ \__
#  +/-        *
#  / \       / \
# -   sqrt  2   a
#  \     \
#   b    -
#     __/ \_
#   **      *
#  /  \    / \
# b    2  4   *
#            / \
#           a   c

以下是printBTree函数:

import functools as fn

def printBTree(node, nodeInfo=None, inverted=False, isTop=True):

   # node value string and sub nodes
   stringValue, leftNode, rightNode = nodeInfo(node)

   stringValueWidth  = len(stringValue)

   # recurse to sub nodes to obtain line blocks on left and right
   leftTextBlock     = [] if not leftNode else printBTree(leftNode,nodeInfo,inverted,False)

   rightTextBlock    = [] if not rightNode else printBTree(rightNode,nodeInfo,inverted,False)

   # count common and maximum number of sub node lines
   commonLines       = min(len(leftTextBlock),len(rightTextBlock))
   subLevelLines     = max(len(rightTextBlock),len(leftTextBlock))

   # extend lines on shallower side to get same number of lines on both sides
   leftSubLines      = leftTextBlock  + [""] *  (subLevelLines - len(leftTextBlock))
   rightSubLines     = rightTextBlock + [""] *  (subLevelLines - len(rightTextBlock))

   # compute location of value or link bar for all left and right sub nodes
   #   * left node's value ends at line's width
   #   * right node's value starts after initial spaces
   leftLineWidths    = [ len(line) for line in leftSubLines  ]                            
   rightLineIndents  = [ len(line)-len(line.lstrip(" ")) for line in rightSubLines ]

   # top line value locations, will be used to determine position of current node & link bars
   firstLeftWidth    = (leftLineWidths   + [0])[0]  
   firstRightIndent  = (rightLineIndents + [0])[0] 

   # width of sub node link under node value (i.e. with slashes if any)
   # aims to center link bars under the value if value is wide enough
   # 
   # ValueLine:    v     vv    vvvvvv   vvvvv
   # LinkLine:    / \   /  \    /  \     / \ 
   #
   linkSpacing       = min(stringValueWidth, 2 - stringValueWidth % 2)
   leftLinkBar       = 1 if leftNode  else 0
   rightLinkBar      = 1 if rightNode else 0
   minLinkWidth      = leftLinkBar + linkSpacing + rightLinkBar
   valueOffset       = (stringValueWidth - linkSpacing) // 2

   # find optimal position for right side top node
   #   * must allow room for link bars above and between left and right top nodes
   #   * must not overlap lower level nodes on any given line (allow gap of minSpacing)
   #   * can be offset to the left if lower subNodes of right node 
   #     have no overlap with subNodes of left node                                                                                                                                 
   minSpacing        = 2
   rightNodePosition = fn.reduce(lambda r,i: max(r,i[0] + minSpacing + firstRightIndent - i[1]), \
                                 zip(leftLineWidths,rightLineIndents[0:commonLines]), \
                                 firstLeftWidth + minLinkWidth)

   # extend basic link bars (slashes) with underlines to reach left and right
   # top nodes.  
   #
   #        vvvvv
   #       __/ \__
   #      L       R
   #
   linkExtraWidth    = max(0, rightNodePosition - firstLeftWidth - minLinkWidth )
   rightLinkExtra    = linkExtraWidth // 2
   leftLinkExtra     = linkExtraWidth - rightLinkExtra

   # build value line taking into account left indent and link bar extension (on left side)
   valueIndent       = max(0, firstLeftWidth + leftLinkExtra + leftLinkBar - valueOffset)
   valueLine         = " " * max(0,valueIndent) + stringValue
   slash             = "\\" if inverted else  "/"
   backslash         = "/" if inverted else  "\\"
   uLine             = "¯" if inverted else  "_"

   # build left side of link line
   leftLink          = "" if not leftNode else ( " " * firstLeftWidth + uLine * leftLinkExtra + slash)

   # build right side of link line (includes blank spaces under top node value) 
   rightLinkOffset   = linkSpacing + valueOffset * (1 - leftLinkBar)                      
   rightLink         = "" if not rightNode else ( " " * rightLinkOffset + backslash + uLine * rightLinkExtra )

   # full link line (will be empty if there are no sub nodes)                                                                                                    
   linkLine          = leftLink + rightLink

   # will need to offset left side lines if right side sub nodes extend beyond left margin
   # can happen if left subtree is shorter (in height) than right side subtree                                                
   leftIndentWidth   = max(0,firstRightIndent - rightNodePosition) 
   leftIndent        = " " * leftIndentWidth
   indentedLeftLines = [ (leftIndent if line else "") + line for line in leftSubLines ]

   # compute distance between left and right sublines based on their value position
   # can be negative if leading spaces need to be removed from right side
   mergeOffsets      = [ len(line) for line in indentedLeftLines ]
   mergeOffsets      = [ leftIndentWidth + rightNodePosition - firstRightIndent - w for w in mergeOffsets ]
   mergeOffsets      = [ p if rightSubLines[i] else 0 for i,p in enumerate(mergeOffsets) ]

   # combine left and right lines using computed offsets
   #   * indented left sub lines
   #   * spaces between left and right lines
   #   * right sub line with extra leading blanks removed.
   mergedSubLines    = zip(range(len(mergeOffsets)), mergeOffsets, indentedLeftLines)
   mergedSubLines    = [ (i,p,line + (" " * max(0,p)) )       for i,p,line in mergedSubLines ]
   mergedSubLines    = [ line + rightSubLines[i][max(0,-p):]  for i,p,line in mergedSubLines ]                        

   # Assemble final result combining
   #  * node value string
   #  * link line (if any)
   #  * merged lines from left and right sub trees (if any)
   treeLines = [leftIndent + valueLine] + ( [] if not linkLine else [leftIndent + linkLine] ) + mergedSubLines

   # invert final result if requested
   treeLines = reversed(treeLines) if inverted and isTop else treeLines

   # return intermediate tree lines or print final result
   if isTop : print("\n".join(treeLines))
   else     : return treeLines                                       

下面是一个使用简单的TreeNode类的输出示例

class TreeNode:

   def __init__(self,rootValue):
       self.value = rootValue
       self.left  = None
       self.right = None

   def addValue(self,newValue):
      if newValue == self.value: return self
      if newValue < self.value:
         if self.left : return self.left.addValue(newValue)
         self.left = TreeNode(newValue)
         return self.left
      if self.right : return self.right.addValue(newValue)
      self.right = TreeNode(newValue)
      return self.right

   def printTree(self):
      printBTree(self,lambda n:(str(n.value),n.left,n.right))      

root = TreeNode(80)

root.addValue(50)
root.addValue(90)
root.addValue(10)
root.addValue(60)
root.addValue(30)
root.addValue(70)
root.addValue(55)
root.addValue(5)
root.addValue(35)
root.addValue(85)

root.printTree()

这将生成以下输出:

#              80
#          ___/  \___
#        50          90
#     __/  \__      /
#   10        60  85
#  /  \      /  \
# 5    30  55    70
#        \
#         35

该函数足够通用,可以处理未存储在对象层次结构中的二叉树结构。下面是一个如何使用它从包含堆树的列表中打印的示例:

def printHeapTree(tree, inverted=False):

    def getNode(index):
        left  = index * 2 + 1
        right = index * 2 + 2
        left  = left  if left  < len(tree) and tree[left]  else None
        right = right if right < len(tree) and tree[right] else None
        return (str(tree[index]), left, right)

    printBTree(0,getNode,inverted)


formula = ["+","4","*",None,None,"2","75"]
printHeapTree(formula)

#   +
#  / \
# 4   *
#    / \
#   2   75

该功能将自动调整较宽标签的缩进:

family = [ "Me","Paul","Rosa","Vincent","Jody","John","Kate"]
printHeapTree(family)

#                Me
#            ___/  \___
#        Paul          Rosa
#        /  \          /  \
# Vincent    Jody  John    Kate

它还可以倒置打印树(适用于家谱):

printHeapTree(family,inverted=True)

# Vincent    Jody  John    Kate
#        \  /          \  /
#        Paul          Rosa
#            ¯¯¯\  /¯¯¯
#                Me
 类似资料:
  • 本文向大家介绍如何打印二叉树每层的节点?相关面试题,主要包含被问及如何打印二叉树每层的节点?时的应答技巧和注意事项,需要的朋友参考一下 考察点:二叉树   实现代码:  

  • 下面是一个二叉查找树,它有一个根节点、一个左节点和一个右节点。代码有效,但我想显示这个二叉查找树,这样我就可以看到图层中的每个节点…这是代码…

  • 我一直在尝试从Node切换到Java,我想知道的一件事是如何以类似于Node显示的格式打印对象,例如二叉树。例如,我的二叉树初始化代码如下: 在节点中,此二叉树将显示如下: 然而在Java,当我做system.out.println(树); 输出->BinaryTree@4554617c 什么是打印我的BinaryTree的正确方法?什么是好方法?有没有一种方法可以用JSON格式打印树?

  • 问题内容: 我想以以下方式打印我的二叉树: 我已经编写了用于插入节点的代码,但是无法编写用于打印树的代码。所以请帮忙。我的代码是: 问题答案: 您正在寻找的是广度优先遍历,它使您可以逐级遍历树。基本上,您使用队列来跟踪需要访问的节点,并在运行时将孩子添加到队列的 后面 (而不是将它们添加到堆栈的 前面 )。首先开始工作。 完成此操作后,您可以找出树具有()的级别,并使用该级别来估计空白。如果要使空

  • 问题内容: 如何在Java中打印二进制树,使输出类似于: 我的节点: 问题答案: 我已经创建了简单的二叉树打印机。你可以根据需要使用和修改它,但是仍然没有对其进行优化。我认为很多事情可以在这里得到改善;) 输出1: 输出2:

  • 我试图打印我的二叉搜索树的每个节点的所有值和深度。我很难想出一种递归计算深度的方法。到目前为止,我有一种仅打印树的每个值的方法。我将不胜感激一些指导,因为我觉得我让它变得比应有的更难。