文章目录
一、二叉查找树
二叉查找树Binary Search Tree
- 性质 :比父节点小的key都出现在左子树 ,比父节点大的key都出现在右子树
- 应用:用二叉查找树保存key-value,实现key的快速搜索。把它作为抽象数据类型-映射 Map的实现方案之一。

二叉查找树的实现
- TreeNode类:定义二叉查找树中使用的节点,包括key和val等属性
- BinarySearchTree类:实例化TreeNode类节点对象,构建二叉查找树。并利用二叉查找树特性,实现抽象数据类型-映射。
_put
辅助方法:如果key比currentNode小,那么_put到左子树;但如果没有左子树,那么key就成为左子节点。反之,_put到右子树或成为右子节点(当没有右子树)。_get
辅助方法:如果currentNode为空,查找结束;如果查找的key等于currentNode的key,返回结果;如果查找的key小于currentNode的key,那么到左子树中查找;反之,到右子树中查找。_delete
辅助方法:从BST中删除一个节点,还要求仍然保持BST的性质,分以下情形:- 这个节点没有子节点:直接删除
- 这个节点只有左子节点:将这个子节点上移,替换被删节点。然后考虑被删节点本身是其父节点的左?还是右子节点?被删节点本身就是根节点?
- 这个节点只有右子节点:将这个子节点上移,替换被删节点。然后考虑被删节点本身是其父节点的左?还是右子节点?被删节点本身就是根节点?
- 这个节点有2个子节点:在被删节点的右子树中,找最小的那个节点,来替换被删节点,这个节点称为"后继"。然后调整后继节点的位置。
python
class TreeNode:
def __init__(self, key, val, l_child=None, r_child=None, parent=None):
self.key = key
self.val = val
self.l_child: TreeNode = l_child
self.r_child: TreeNode = r_child
self.parent: TreeNode = parent
def __iter__(self):
"""实现for迭代:中序遍历的迭代"""
if self:
if self.has_l_child():
for elem in self.l_child:
yield elem
yield self.key
if self.has_r_child():
for elem in self.r_child:
yield elem
def has_l_child(self):
# return True if self.l_child else False
return False if self.l_child == None else True
def has_r_child(self):
return True if self.r_child else False
def is_l_child(self):
return self.parent and self.parent.l_child == self
def is_r_child(self):
return self.parent and self.parent.r_child == self
def is_root(self):
return not self.parent
def is_leaf(self):
return not (self.l_child or self.r_child)
def has_any_children(self):
return self.l_child or self.r_child
def has_both_children(self):
return self.l_child and self.r_child
def replace_node_data(self, key, val, lc, rc):
self.key = key
self.val = val
self.l_child = lc
self.r_child = rc
def find_successor(self):
"""寻找后继节点"""
succ = None
if self.has_r_child():
succ = self.r_child.find_min()
return succ
def find_min(self):
"""寻找当前节点下最小节点:一直向左子节点寻找"""
current = self
while current.has_l_child():
current = current.l_child
return current
def splice_out(self):
"""将后继节点移出"""
if self.is_leaf(): # 如果当前是叶子节点
if self.is_l_child():
self.parent.l_child = None
else:
self.parent.r_child = None
elif self.has_r_child: # 如果当前节点有右子节点
if self.is_l_child():
self.r_child.parent = self.parent
self.parent.l_child = self.r_child
else:
self.r_child.parent = self.parent
self.parent.r_child = self.r_child
# 后继节点不会有左子节点。因为后继节点,是被删节点的右子树中最小的。
class BinarySearchTree:
def __init__(self):
self.root: TreeNode = None
self.size = 0
def __len__(self):
"""支持调用len()获得数据项的数量"""
return self.size
def __iter__(self):
"""实现for迭代"""
return self.root.__iter__()
def __setitem__(self, key, val):
"""支持[]形式赋值"""
self.put(key, val)
def __getitem__(self, key):
"""支持[]索引获取值"""
return self.get(key)
def __contains__(self, key):
"""支持归属判断运算符in"""
return True if self._get(key, self.root) else False
def __delitem__(self, key):
"""实现del语法删除"""
return self.delete(key)
def put(self, key, val):
"""插入key val,构造BST"""
# 如果树不为空时,调用_put()插入到BST;否则成为BST的根
if self.root:
self._put(key, val, self.root)
else:
self.root = TreeNode(key, val)
self.size += 1
def _put(self, key, val, cur_node: TreeNode):
# 如果key比currentNode小,那么_put到左子树;否则,_put到左子树。
# print(f"-----{cur_node.key,cur_node.val}--------")
if key < cur_node.key:
if cur_node.has_l_child():
self._put(key, val, cur_node.l_child)
else:
cur_node.l_child = TreeNode(key, val, parent=cur_node)
else:
if cur_node.has_r_child():
self._put(key, val, cur_node.r_child)
else:
cur_node.r_child = TreeNode(key, val, parent=cur_node)
def get(self, key):
if self.root:
res_node = self._get(key, self.root)
return res_node.val if res_node else None
else:
return None
def _get(self, key, cur_node: TreeNode) -> TreeNode:
if cur_node == None:
return
# 如果查找的key等于当前节点的key,返回结果;
# 如果查找的key小于当前节点的key,在左孩子节点查找;否则,在右孩子查找
if key == cur_node.key:
return cur_node
elif key < cur_node.key:
return self._get(key, cur_node.l_child)
else:
return self._get(key, cur_node.r_child)
def delete(self, key):
if self.root == None:
return False
if self.size == 1:
if self.root.key == key:
self.root = None
self.size = 0
return True
else:
return False
# 找节点,如果找不到返回False,否则删除节点
res_node = self._get(key, self.root)
if res_node == None:
return False
else:
self._delete(res_node)
self.size -= 1
def _delete(self, cur_node: TreeNode):
# 如果当前节点是叶子节点
if cur_node.is_leaf():
if cur_node.is_l_child():
cur_node.parent.l_child = None
else:
cur_node.parent.r_child = None
return
# 如果当前节点只有左子节点
if cur_node.has_l_child() and not cur_node.has_r_child():
if cur_node.is_l_child(): # 如果当前节点是父节点的左节点
cur_node.l_child.parent = cur_node.parent
cur_node.parent.l_child = cur_node.l_child
elif cur_node.is_r_child(): # 如果当前节点是父节点的右节点
cur_node.l_child.parent = cur_node.parent
cur_node.parent.r_child = cur_node.l_child
else: # 如果当前节点是根节点
cur_node.l_child.parent = None
self.root = cur_node.l_child
return
# 如果当前节点只有右子节点
if cur_node.has_r_child() and not cur_node.has_l_child():
if cur_node.is_l_child():
cur_node.r_child.parent = cur_node.parent
cur_node.parent.l_child = cur_node.r_child
elif cur_node.is_r_child():
cur_node.r_child.parent = cur_node.parent
cur_node.parent.r_child = cur_node.r_child
else:
cur_node.r_child.parent = None
self.root = cur_node.r_child
return
# 如果当前节点有左右两个子节点
if cur_node.has_both_children():
succ = cur_node.find_successor()
succ.splice_out()
cur_node.key = succ.key
cur_node.val = succ.val
二、二叉查找树测试
- 依次将下面6个节点插入到二叉查找树,查看二叉查当前数据项数量
- 在二叉查找树上查找key
- 在二叉查找树上删除key,再进行查找
python
bst = BinarySearchTree()
bst[54] = "cat"
bst[26] = "dog"
bst[93] = "lion"
bst[17] = "tiger"
bst[77] = "bird"
bst[31] = "cow"
print(len(bst))
print(bst[20])
print(bst[26])
del bst[26]
print(bst[26])
print(bst[93])
print(len(bst))
### 输出结果
6
None
dog
None
lion
5

三、二叉查找树算法分析
- 查找性能决定因素在于二叉搜索树的高度(最大层次),
- 如果key的列表是随机分布的话,那么大于和小于根节点key的键值大致相等。此时,n个节点的树高度是
log2n
,这样的树称为平衡树 - 受key列表分布和插入顺序影响,put方法最优性能为O(log2n),最差性能为O(n)
您正在阅读的是《数据结构与算法Python版》专栏!关注不迷路~