最近秋招差不多结束了,这几个月复习了不少东西,记录一下用python构造二叉树以及相关的操作,与二叉树相关的操作大多数都可以用递归的方法来解决。本文将记录二叉树的前序遍历、中序遍历、后序遍历、层次遍历、二叉树排序、查询、反转,以及笔试中常遇到的二叉树相关操作。
首先我们来创建一个二叉树节点类,并加入插入节点的方法。
class BTree:
def __init__(self,value):
self.left = None
self.data = value
self.right = None
self.parent = None
def insetleft(self,value):
self.left = BTree(value)
self.left.parent = self
return self.left
def insertright(self,value):
self.right = BTree(value)
self.right.parent = self
return self.right
def show(self):
print(self.data)
# 手动创建一个二叉树:root是根节点,a、b分别为root的左右子节点,c、d分别为a的左右子节点,e是b的左子节点
root = BTree('R')
a = root.insertleft('A')
b = root.insertright('B')
c = a.insertleft('C')
d = a.insertright('D')
e = b.insertleft('E')
二叉树中最常见的就是它的三种遍历方式:前序遍历、中序遍历、后序遍历。前序遍历的方式为:中-左-右;中序遍历的方式为:左-中-右;后序遍历的方式为:左-右-中。需要注意的是:对于一棵排序二叉树,它的中序遍历就是各元素从小到大的排序。使用递归的方式来对二叉树进行三种遍历,三个函数的思路完全一样,十分简单。
# 前序遍历函数
def preorder(node):
if node.data:
node.show()
if node.left:
preorder(node.left)
if node.right:
preorder(node.right)
# 中序遍历函数
def inorder(node):
if node.data:
if node.left:
inorder(node.left)
node.show()
if node.right:
inorder(node.right)
# 后序遍历函数
def postorder(node):
if node.data:
if node.left:
postorder(node.left)
if node.right:
postorder(node.right)
node.show()
>>>preorder(root)
R
A
C
D
B
E
>>>inorder(root)
C
A
D
R
E
B
>>>postorder(root)
C
D
A
E
B
R
接下来我们写一个列表的二叉树排序操作,创建一个insert()函数,用于将元素插入二叉树中,借助insert()函数对任意list进行二叉树排序,生成一个排序二叉树,最后使用中序遍历得到排序后的二叉树。
def insert(node,value):
if value>node.data:
if node.right == None:
node.insertright(value)
else:
insert(node.right,value)
else:
if node.left == None:
node.insertleft(value)
else:
insert(node.left,value)
# 为list进行排序
L = [4,3,6,13,61,38,22,41]
new_Root= BTree(L[0]) # 创建根节点
for i in range(1,len(L)):
insert(new_Root,L[i])
# 遍历完成后二叉树构建完成
inorder(new_Root)
3
4
6
13
22
38
41
61
写一个在排序二叉树中进行查询的函数search(),这里用了非递归的方法
def search(node,k):
while node!=None:
if k<node.data:
node = node.left
elif k>node.data:
node = node.right
else:
return k
return 0
调用一下:
print(search(new_Root,29))
0
print(search(new_Root,41))
41
写一个遍历二叉树根节点到所有子节点的通路并对每条通路求和的函数:
def cal_road(node,list): # 遍历二叉树所有分支,需要给函数传入一个存放结果的空列表
if (node.left==None and node.right==None):
node.show()
list.append(node.data)
if node.left!=None:
node.left.data = node.data + node.left.data
cal_road(node.left,list)
if node.right!=None:
node.right.data = node.data + node.right.data
cal_road(node.right,list)
验证一下,这里在函数定义时也可以不传list,这样就直接print出来结果,但是结果不会保存:
result_L = []
cal_road(new_Root, result_L)
7
144
163
print(result_L)
[7, 144, 163]
在做各大公司的笔试题的时候,经常遇到一道题:已知二叉树的前序遍历和中序遍历,求二叉树的后序遍历。思路还是递归,首先根据中-左-右和左-中-右来还原二叉树,所以前序遍历的第一个元素就是根节点,在中序遍历中出现在根节点之前的就是它的左子树,出现在根节点之后的就是它的右子树,然后依次对左右子树递归使用这种方法直到重建二叉树。
很有意思,在牛客网刷题的时候遇到一道编程题,即写一个函数,可以根据前序遍历和中序遍历求得后序遍历,那么来写一下吧。我们定义一个rebuild()函数,该函数可以根据前序遍历和中序遍历重建二叉树,返回值是重建二叉树的根节点。
def rebuild(pre,tin):
if len(pre) == 0:
return
if len(pre) == 1:
return BTree(pre[0])
if len(pre) > 1:
node = BTree(pre[0])
node.left = rebuild(pre[1:tin.index(pre[0])+1], tin[:tin.index(pre[0])])
node.right = rebuild(pre[tin.index(pre[0])+1:], tin[tin.index(pre[0])+1:])
return node
例行测试:
pre = [1,2,4,7,3,5,6,8]
tin = [4,7,2,1,5,3,8,6]
node = rebuild(pre,tin)
print(inorder(node)) # 打印树的中序遍历
4
7
2
1
5
3
8
6
None
print(postorder(node)) # 打印树的后序遍历
7
4
2
5
8
6
3
1
None
这里最后都有一个None是因为在递归的最后一步len(pre)==0时,会建立一个子节点,返回了一个None给这个节点。
写一个二叉树反转(镜像)的函数,这个好像也是在牛客里看到的编程题,依然是用递归:
def reverse_tree(node,new_node):
if node.right:
new_node.left = BTree(node.right.data)
reverse_tree(node.right,new_node.left)
if node.left:
new_node.right = BTree(node.left.data)
reverse_tree(node.left,new_node.right)
return
测试:
new_node = BTree(node.data) # 首先将原来树的根节点重新赋值给一棵新树的根节点
reverse_tree(node,new_node) # 调用反转函数
print(inorder(new_node)) # 看一下新树的中序遍历,应当等于原树中序遍历的倒序
6
8
3
5
1
2
7
4
None
最后一个,也是最难想到的,就是二叉树的层次遍历,层次遍历无法用递归来写(终于可以抛弃递归了T^T),需要借助一个空列表来实现。它的实现算法是这样的:
- 1.初始化空列表L
- 2.根节点入队尾
- 3.while(len(L)!=0):
- 将L中的首元素L[0]的左右子节点入队尾
- 取出L中首元素L[0]
- 4..until len(L)==0
def gradation(node):
result_list = []
list = []
list.append(node)
while len(list)!=0:
node = list[0]
del list[0]
result_list.append(node.data)
if node.left:
list.append(node.left)
if node.right:
list.append(node.right)
return result_list
来,验证一下:
print(gradation(node))
[1, 2, 3, 4, 5, 6, 7, 8]
收工。