如何在Python中将二叉树打印为节点结构
我需要将字符串数学表达式转换为二叉树,并对树的节点进行排序,这样左边的子节点总是比右边的子节点小。我想按以下顺序打印二叉树如何在Python中将二叉树打印为节点结构,python,binary-tree,tree-traversal,Python,Binary Tree,Tree Traversal,我需要将字符串数学表达式转换为二叉树,并对树的节点进行排序,这样左边的子节点总是比右边的子节点小。我想按以下顺序打印二叉树 例如考虑数学表达式2 * 75/4。buildParseTree将字符串表达式转换为树,PrintNodeInLevel会重新排列节点,使左子级在每个级别上都小于右子级。操作数
例如考虑数学表达式2 * 75/4。buildParseTree将字符串表达式转换为树,PrintNodeInLevel会重新排列节点,使左子级在每个级别上都小于右子级。操作数<运算符和运算符的顺序为“+”<”-“<”*“<”/”。如果树的结构是这样的
+
/\
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
首先,您应该阅读python的PEP8代码约定,因为它说函数、属性和变量应该在snake_的情况下 你是以一种迭代的方式打印的,这意味着你不能以等腰三角形打印,因为你不知道树的最底部的大小,以一种迭代的方式你应该像一个90度角的三角形一样打印
或者,您可以将所有信息收集到列表或字符串中,然后将其格式化并打印出来。首先考虑头部,然后是中间有线条的子对象。首先,您应该阅读python的PEP8代码约定,因为它说函数、属性和变量应该在snake_的情况下 你是以一种迭代的方式打印的,这意味着你不能以等腰三角形打印,因为你不知道树的最底部的大小,以一种迭代的方式你应该像一个90度角的三角形一样打印
或者,您可以将所有信息收集到列表或字符串中,然后将其格式化并打印出来。考虑头部,然后是中间有线条的子对象。我创建了一个函数,用于打印任何二叉树结构 它非常通用,只需要一个起始节点根和一个函数或lambda即可获得标签和左/右子节点: 您通常会在节点类上这样使用它:
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+/-sqrtb**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()
该函数足够通用,可以处理未存储在对象层次结构中的二叉树结构。下面是如何使用它从包含堆树的列表打印的示例:
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
它还可以将树倒置打印,以适合于家谱:
printHeapTree(family,inverted=True)
# Vincent Jody John Kate
# \ / \ /
# Paul Rosa
# ¯¯¯\ /¯¯¯
# Me
这是我创建的一个函数,用于打印任何二叉树结构 它非常通用,只需要一个起始节点根和一个函数或lambda即可获得标签和左/右子节点: 您通常会在节点类上这样使用它:
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+/-sqrtb**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()
该函数足够通用,可以处理未存储在对象层次结构中的二叉树结构。下面是如何使用它从包含堆树的列表打印的示例:
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
它还可以将树倒置打印,以适合于家谱:
printHeapTree(family,inverted=True)
# Vincent Jody John Kate
# \ / \ /
# Paul Rosa
# ¯¯¯\ /¯¯¯
# Me
一个简单而粗糙的例子:
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)
一个简单而粗糙的例子:
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)
谢谢你的评论。我是python新手。我在试你建议的晚一点的。谢谢你的评论。我是python新手。我在试你建议的晚一点的。谢谢