python實現二叉樹及相關操作
最近秋招差不多結束了,這幾個月複習了不少東西,記錄一下用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]
收工。