1 为什么要使用二叉搜索树?
我们知道搜索算法有顺序搜索,二分搜索,还有散列表(也叫哈希表),而二叉搜索树是另一种搜索算法,利用二叉树的结构可以提高搜索效率。
2 二叉搜索树的性质
二叉搜索性:小于父节点的键都在左子树中,大于父节点的键则都在右子树中。左子树的所有键都小于根节点的键,右子树的所有键则都大于根节点的键。
3 代码实现
Python版本:
有两个类分别是BinarySearchTree和TreeNode。首先看看TreeNode,其提供了很多辅助函数,大大简化了BinarySearchTree类的工作。
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 15 16:38:33 2021
@author: Ethan
"""
class TreeNode:
def __init__(self, key, val, left=None, right=None, parent=None):
self.key = key
self.payload = val
self.leftChild = left
self.rightChild = right
self.parent = parent
##返回当前节点的左子节点
def hasLeftChild(self):
return self.leftChild
##返回当前节点的右子节点
def hasRightChild(self):
return self.rightChild
##当前节点是左子节点
def isLeftChild(self):
return self.parent and self.parent.leftChild == self
##当前节点是右子节点
def isRightChild(self):
return self.parent and self.parent.rightChild == self
##判断是否为根节点
def isRoot(self):
return not self.parent
##判断是否为叶子节点
def isLeaf(self):
return not (self.leftChild or self.rightChild)
##判断是否有子节点
def hasAnyChildren(self):
return self.leftChild or self.rightChild
##判断是否有两个子节点
def hasBothChildren(self):
return self.leftChild and self.rightChild
def replaceNodeData(self, key, val, lc, rc):
self.key = key
self.payload = val
self.leftChild = lc
self.rightChild = rc
if self.hasLeftChild():
self.leftChild.parent = self
if self.hasRightChild():
self.rightChild.parent = self
def __iter__(self):
if self:
if self.hasLeftChild():
for elem in self.leftChild:
yield elem
yield self.key
if self.hasRightChild():
for elem in self.rightChild:
yield elem
然后就是BinarySearchTree类,采用的是节点与引用来实现的二叉搜索树
# -*- coding: utf-8 -*-
"""
Created on Wed Sep 15 15:48:08 2021
@author: Ethan
"""
from TreeNodeClass import TreeNode
class BinarySearchTree:
def __init__(self):
self.root = None
self.size = 0
def length(self):
return self.size
def __len__(self):
return self.size
def __iter__(self):
return self.root.__iter__()
##往映射中加入一个新的键值对,如果键已经存在,就用新值替换旧值
def put(self, key, val):
if self.root == None: ##检查树是否有根节点,若没有,就创建一个TreeNode,并将其作为树的根节点
self.root = TreeNode(key, val)
else: ##若树有根节点,调用私有的递归辅助函数_put在树中搜索
self._put(key, val, self.root)
self.size = self.size + 1
def _put(self, key, val, currentNode):
if key < currentNode.key: ##比较新键与当前节点的键,若新键更小,搜索左子树
if currentNode.hasLeftChild():
self._put(key, val, currentNode.leftChild)
else: ##当没有可供搜索的左子节点时,就说明找到了新键的插入位置
currentNode.leftChild = TreeNode(key, val, parent=currentNode)
else: ##若新键更小,搜索右子树
if currentNode.hasRightChild():
self._put(key, val, currentNode.rightChild)
else: ##当没有可供搜索的右子节点时,就说明找到了新键的插入位置
currentNode.rightChild = TreeNode(key, val, parent=currentNode)
##调用put方法重载[]运算符
def __setitem__(self, k, v):
self.put(k, v)
##返回key对应的值,如果key不存在,则返回None
def get(self, key):
if self.root == None:
return None
else:
ret = self._get(key, self.root)
if ret!=None:
return ret.payload
else:
return None
def _get(self, key, currentNode):
if not currentNode:
return None
elif currentNode.key == key:
return currentNode
elif key < currentNode.key:
return self._get(key, currentNode.leftChild)
else:
return self._get(key, currentNode.rightChild)
##调用get方法重载[]运算符
def __getitem__(self, key):
return self.get(key)
##实现in操作
def __contains__(self, key):
if self._get(key, self.root):
return True
else:
return False
##删除键值对
def delete(self, key):
if self.size > 1:
nodeToRemove = self._get(key, self.root)
if nodeToRemove != None:
self.remove(nodeToRemove)
self.size = self.size - 1
else:
raise KeyError('Error, key not in trre')
elif self.size == 1 and self.root.key == key:
self.root = None
self.size = self.size - 1
else:
raise KeyError('Error, key not in trre')
def __delitem__(self, key):
self.delete(key)
def remove(self, currentNode):
if currentNode.isLeaf(): ##当前节点为叶子节点
if currentNode == currentNode.parent.leftChild:
currentNode.parent.leftChild = None
else:
currentNode.parent.rightChild = None
elif currentNode.hasBothChildren(): ##当前节点有两个子节点
succ = currentNode.findSuccessor()
succ.spliceOut()
currentNode.key = succ.key
currentNode.payload = succ.payload
else: ##当前节点只有一个子节点
if currentNode.hasLeftChild(): ##当前节点有左子节点
if currentNode.isLeftChild(): ##当前节点是左子节点
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.leftChild
elif currentNode.isRightChild(): ##当前节点是右子节点
currentNode.leftChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.leftChild
else: ##当前节点是根节点
currentNode.replaceNodeData(currentNode.leftChild.key, currentNode.leftChild.payload,
currentNode.leftChild.leftChild, currentNode.rightChild.rightChild)
else: ##当前节点有左子节点
if currentNode.isLeftChild(): ##当前节点是左子节点
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.leftChild = currentNode.rightChild
elif currentNode.isRightChild(): ##当前节点是右子节点
currentNode.rightChild.parent = currentNode.parent
currentNode.parent.rightChild = currentNode.rightChild
else: ##当前节点是根节点
currentNode.replaceNodeData(currentNode.rightChild.key, currentNode.rightChild.payload,
currentNode.rightChild.leftChild, currentNode.rightChild.rightChild)
##寻找后继节点
def findSuccessor(self):
succ = None
if self.hasRightChild():
succ = self.rightChild.findMin()
else:
if self.parent:
if self.isLeftChild():
succ = self.parent
else:
self.parent.rightChild = None
succ = self.parent.findSuccessor()
self.parent.rihgtChild = self
return succ
def findMin(self):
current = self
while current.hasLeftChild():
current = current.leftChild
return current
def spliceOut(self):
if self.isleaf():
if self.isLeftChild():
self.parent.leftChild = None
else:
self.parent.rightChild = None
elif self.hasAnyChildren():
if self.hasLeftChild():
if self.isLeftChild():
self.parent.leftChild = self.leftChild
else:
self.parent.rightChild = self.leftChild
self.leftChild.parent = self.parent
else:
if self.isLeftChild():
self.parent.leftChild = self.rightChild
else:
self.parent.rightChild = self.rightChild
self.rightChild .parent = self.parent
if __name__ == "__main__":
bst = BinarySearchTree()
bst.put(5, 8)
print(len(bst))
print(bst.get(5))
print(bst.get(4))
4 参考资料
《Python数据结构与算法分析》