1. 定義
二叉查找樹(Binary Search Tree),又稱爲二叉搜索樹、二叉排序樹。其或者是一棵空樹;或者是具有以下性質的二叉樹:
- 若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值
- 若右子樹不空,則右子樹上所有結點的值均大於或等於它的根結點的值
- 左、右子樹也分別爲二叉排序樹
圖 1
樹中節點的定義如下:
class Node:
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
2. 查找與插入
當二叉查找樹不爲空時:
- 首先將給定值與根結點的關鍵字比較,若相等,則查找成功
- 若小於根結點的關鍵字值,遞歸查左子樹
- 若大於根結點的關鍵字值,遞歸查右子樹
- 若子樹爲空,查找不成功
二叉排序樹是一種動態樹表。其特點是:樹的結構通常不是一次生成的,而是在查找過程中,當樹中不存在關鍵字等於給定值的結點時再進行插入。新插入的結點一定是一個新添加的葉子結點,並且是查找不成功時查找路徑上訪問的最後一個結點的左孩子或右孩子結點。如下圖所示:
插入與搜索的代碼:
# 搜索
def search(self, node, parent, data):
if node is None:
return False, node, parent
if node.data == data:
return True, node, parent
if node.data > data:
return self.search(node.lchild, node, data)
else:
return self.search(node.rchild, node, data)
# 插入
def insert(self, data):
flag, n, p = self.search(self.root, self.root, data)
if not flag:
new_node = Node(data)
if data > p.data:
p.rchild = new_node
else:
p.lchild = new_node
3. 刪除
二叉查找樹的刪除操作分爲三種情況:
- 如果待刪除的節點是葉子節點,那麼可以立即被刪除,如下圖所示:
- 如果節點只有一個兒子,則將此節點parent的指針指向此節點的兒子,然後刪除節點,如下圖所示:
- 如果節點有兩個兒子,則將其右子樹的最小數據代替此節點的數據,並將其右子樹的最小數據刪除,如下圖所示:
刪除節點的代碼:
# 刪除
def delete(self, root, data):
flag, n, p = self.search(root, root, data)
if flag is False:
print "無該關鍵字,刪除失敗"
else:
if n.lchild is None:
if n == p.lchild:
p.lchild = n.rchild
else:
p.rchild = n.rchild
del n
elif n.rchild is None:
if n == p.lchild:
p.lchild = n.lchild
else:
p.rchild = n.lchild
del n
else: # 左右子樹均不爲空
pre = n.rchild
if pre.lchild is None:
n.data = pre.data
n.rchild = pre.rchild
del pre
else:
next = pre.lchild
while next.lchild is not None:
pre = next
next = next.lchild
n.data = next.data
pre.lchild = next.rchild
del next
4. 遍歷
1. 先序遍歷
訪問順序如下:
- 訪問根節點
- 先序遍歷左子樹
- 先序遍歷右子樹
以圖 1爲例,訪問順序爲:4,2,1,3,5,6
# 先序遍歷
def preOrderTraverse(self, node):
if node is not None:
print node.data,
self.preOrderTraverse(node.lchild)
self.preOrderTraverse(node.rchild)
2. 中序遍歷
訪問順序如下:
- 中序遍歷左子樹
- 訪問根節點
- 中序遍歷右子樹
以圖 1爲例,訪問順序爲:1,2,3,5,7,9
# 中序遍歷
def inOrderTraverse(self, node):
if node is not None:
self.inOrderTraverse(node.lchild)
print node.data,
self.inOrderTraverse(node.rchild)
3. 後序遍歷
訪問順序如下:
- 後序遍歷左子樹
- 後序遍歷右子樹
- 訪問根節點
以圖 1爲例,訪問順序爲:1,3,2,9,7,5
# 後序遍歷
def postOrderTraverse(self, node):
if node is not None:
self.postOrderTraverse(node.lchild)
self.postOrderTraverse(node.rchild)
print node.data,
5. 完整代碼
# encoding: utf-8
class Node:
def __init__(self, data):
self.data = data
self.lchild = None
self.rchild = None
class BST:
def __init__(self, node_list):
self.root = Node(node_list[0])
for data in node_list[1:]:
self.insert(data)
# 搜索
def search(self, node, parent, data):
if node is None:
return False, node, parent
if node.data == data:
return True, node, parent
if node.data > data:
return self.search(node.lchild, node, data)
else:
return self.search(node.rchild, node, data)
# 插入
def insert(self, data):
flag, n, p = self.search(self.root, self.root, data)
if not flag:
new_node = Node(data)
if data > p.data:
p.rchild = new_node
else:
p.lchild = new_node
# 刪除
def delete(self, root, data):
flag, n, p = self.search(root, root, data)
if flag is False:
print "無該關鍵字,刪除失敗"
else:
if n.lchild is None:
if n == p.lchild:
p.lchild = n.rchild
else:
p.rchild = n.rchild
del p
elif n.rchild is None:
if n == p.lchild:
p.lchild = n.lchild
else:
p.rchild = n.lchild
del p
else: # 左右子樹均不爲空
pre = n.rchild
if pre.lchild is None:
n.data = pre.data
n.rchild = pre.rchild
del pre
else:
next = pre.lchild
while next.lchild is not None:
pre = next
next = next.lchild
n.data = next.data
pre.lchild = next.rchild
del p
# 先序遍歷
def preOrderTraverse(self, node):
if node is not None:
print node.data,
self.preOrderTraverse(node.lchild)
self.preOrderTraverse(node.rchild)
# 中序遍歷
def inOrderTraverse(self, node):
if node is not None:
self.inOrderTraverse(node.lchild)
print node.data,
self.inOrderTraverse(node.rchild)
# 後序遍歷
def postOrderTraverse(self, node):
if node is not None:
self.postOrderTraverse(node.lchild)
self.postOrderTraverse(node.rchild)
print node.data,
a = [49, 38, 65, 97, 60, 76, 13, 27, 5, 1]
bst = BST(a) # 創建二叉查找樹
bst.inOrderTraverse(bst.root) # 中序遍歷
bst.delete(bst.root, 49)
print
bst.inOrderTraverse(bst.root)