一、什么是二叉排序树?
二叉排序树要么是一棵空二叉树,要么是具有如下特点的二叉树:
- 二叉排序树中,如果其根结点有左子树,那么左子树上所有节点的值都小于根结点的值;
- 二叉排序树中,如果其根结点有右子树,那么右子树上所有节点的值都大于根结点的值;
- 二叉排序树的左右子树也要求都是二叉排序树。
如下图所示:
二、二叉排序树的Python实现
二叉排序树的插入
插入: 从根结点开始逐个与关键字进行比较,小于根结点去左边,大于根结点去右边,碰到子树为空的情况指向关键字。
class Node(object):
"""二叉树节点的封装"""
def __init__(self, element=None, lchild=None, rchild=None):
self.element = element
self.lchild = lchild
self.rchild = rchild
def __str__(self):
return '<node:%d>' % (self.element)
class BinarySortTree(object):
"""二叉排序树的封装"""
def __init__(self):
self.root = None
def is_empty(self):
return self.root == None
def __add__(self, item):
"""
树里面插入元素:
1. 如果为空二叉树,将根节点指向插入元素的节点
2. 如果不为空,与根结点进行大小比较,大于根结点寻找右子树,小于根结点寻找左子树
:param item:
:return:
"""
node = Node(item)
if self.is_empty():
self.root = node
bt = self.root
while True:
rootItem = bt.element
# 如果节点元素大于插入元素,寻找左子树
if rootItem > item:
if bt.lchild == None:
bt.lchild = node
bt = bt.lchild
# 如果节点的元素小于插入元素,寻找右子树
elif rootItem < item:
if bt.rchild == None:
bt.rchild = node
bt = bt.rchild
else:
return
def breadth_travel(self):
"""利用队列实现树的层次遍历"""
if self.is_empty():
return
queue = []
queue.append(self.root)
while queue:
cur = queue.pop(0)
print(cur.element, end=',')
if cur.lchild != None:
queue.append(cur.lchild)
if cur.rchild != None:
queue.append(cur.rchild)
print()
二叉排序树的查找
查找: 对比节点的值和关键字,相等则表明找到了,输出该节点;小于节点的值,则往节点的左子树去寻找,大于节点的值,则往右子树去寻找,这样递归下去,最后返回布尔值或者找到的节点。
def searchDetail(self, root, parent, key):
"""
搜索指定元素是否在树里面
:param root: 根结点
:param parent: 父节点
:param key: 用户搜索的元素
:return:返回parent是为了方便下边的删除
"""
if self.is_empty():
return False, root, parent
while root:
# 如果找到的节点元素和用户搜索的相等,直接返回节点对象
if root.element == key:
return True, root, parent
# 如果找到的节点元素大于用户搜索的值,往节点的左子树继续寻找
elif root.element > key:
parent = root
root = root.lchild
# 如果找到的节点元素小于用户搜索的值,往节点的右子树继续寻找
else:
parent = root
root = root.rchild
return False, root, None
二叉排序树的删除
1、 删除节点为叶子节点
删除的节点没有左子树也没有右子树,也就是删除的节点为叶子节点。有两种情况:
1. 该叶子节点为二叉排序树的根结点,也就是二叉排序树中只有一个结点,只需要将root的指针置为空即可
2. 该叶子节点有父节点,将父节点的连接该删除节点的指针置为空即可。
2、删除的节点只有左子树或者只有右子树
删除节点后,将父节点指针指向子树即可
3、同时存在左右子树
如果同时存在左右子树,则可以将二叉排序树进行中序遍历,取将要被删除的节点的前驱或者后继节点替代这个被删除的节点的位置。
方法一: 令结点 p 的左子树为其双亲结点的左子树;结点 p 的右子树为其自身直接前驱结点的右子树,简单来说就是选择删除节点的左子树的最大值来补上。
同理,如果删除节点是根结点的右子树节点,令节点p的右子树为其双亲节点的右子树,令节点p的左子树为自身后继节点的左子树。简单来说就是另选择删除节点的右子树的最小值来补上。
def __delete__(self, key):
"""
删除二叉排序树的节点元素:
1. 如果删除的是叶子节点,直接删除
2. 如果只有左子树或者右子树,则删除节点后,将子树链接到父节点即可
3. 既存在左子树又存在右子树,两种方法解决
:param key:
:return:
"""
# 查找删除元素对应的节点,获取其是否存在,获取节点和其父节点
isExist, node, parentNode = self.searchDetail(self.root, None, key)
# 如果节点不存在
if not isExist:
print('要删除的元素%d不存在' %(node.element))
return
# 如果不存在根
if not self.root:
print('thr tree is null')
return
# 如果处理的是根节点,不处理
if node == self.root:
print('不能删除根节点')
return
# 1.如果删除的是叶子节点,根据其父节点删除
if not node.lchild and not node.rchild:
# 判断是父节点的右孩子还是左孩子
if parentNode.rchild == node:
parentNode.rchild = None
if parentNode.lchild == node:
parentNode.rchild = None
# 2.如果只存在右子树或者左子树,将节点的子节点链接到父节点上,将节点删除
# 如果是右子树
if not node.lchild and node.rchild:
parentNode.rchild = node.rchild
# 如果是左子树
if not node.rchild and node.lchild:
parentNode.lchild = node.lchild
# 3.左右子树皆存在
if node.lchild and node.rchild:
# 分类讨论,删除的是父节点的左孩子还是右孩子
# 如果找到了node的直接父节点,令节点node的左子树为其双亲节点的左子树,
# 将node的左子树的右子树的最右节点指向node的右子树
if parentNode.lchild == node:
parentNode.lchild = node.lchild
prevNode = node.lchild
while prevNode.rchild:
prevNode = prevNode.rchild
# prevNode指的是node节点的直接前驱(中序遍历时,node节点前面的节点)
prevNode.rchild = node.rchild
# 如果删除的是父节点的右孩子,将父节点的右孩子指向node节点的右孩子
# 将node节点的右孩子的左子树的最左节点指向node的左子树
elif parentNode.rchild == node:
parentNode.rchild = node.rchild
prevNode = node.rchild
# prevNode指的是node节点的直接后继(中序遍历时,node节点后面的节点)
while prevNode.lchild:
prevNode = prevNode.lchild
prevNode.lchild = node.lchild
方法二: 用结点 p 的直接前驱(或直接后继)来代替结点 p,同时在二叉排序树中对其直接前驱(或直接后继)做删除操作。在对左图进行中序遍历时,得到的结点 p 的直接前驱结点为结点 s,所以直接用结点 s 覆盖结点 p
方法二的代码类似于方法一,这里就不再编写了。
大体思路:
先找到node节点的左子树的右子树的最右节点(就是找到小于删除节点但最接近删除节点大小的节点),让这个最右节点的父节点的右孩子指向最右节点的左孩子,让node的父节点的左孩子指向node节点的左子树的右子树的最右节点,这个最右节点的左孩子指向node节点的左孩子,右孩子指向node节点的右孩子。
测试代码:
if __name__ == '__main__':
binarySortedTree = BinarySortTree()
nums = [6, 4, 2, 3, 5, 8, 7, 9]
for num in nums:
binarySortedTree.__add__(num)
# binarySortedTree.__add__(4)
# binarySortedTree.__add__(6)
binarySortedTree.breadth_travel()
root = binarySortedTree.root
print('详细搜索....')
isExist, node, parentNode = binarySortedTree.searchDetail(root, None, 7)
print('搜索结果:',isExist)
print("找到的节点:" ,node)
print('搜索节点的父节点:', parentNode)
# binarySortedTree.__delete__(2)
# binarySortedTree.breadth_travel()
binarySortedTree.__delete__(8)
binarySortedTree.breadth_travel()
6,4,8,2,5,7,9,3,
详细搜索....
搜索结果: True
找到的节点: <node:7>
搜索节点的父节点: <node:8>
6,4,9,2,5,7,3,