1、基本概念与完整代码
二叉搜索树事一颗二叉树且满足性质:设x是二叉搜索树的一个节点,如果y是x左子树的一个节点,那么y.key <= x.key,如果y是x右子树的一个节点,那么y.key >= x.key。通俗的讲就是,树中每一个节点的左子树里面的所有节点都比该节点小,但是该节点的右子树里面的所有节点都比该节点大(简称:左小右大)。
二叉搜索树完全代码:
class BiTreeNode():
def __init__(self,data):
self.data = data
self.lchild = None # 左孩子
self.rchild = None # 右孩子
self.parent = None
class BST():
def __init__(self,li=None):
self.root = None
if li:
for val in li:
self.insert_no_sec(val)
def insert(self,node,val): # 使用递归的方式插入
if not node : # 如果要插入的节点node为空,就创建一个节点,内容是val,其实这个是递归的终止条件,在最后的时候把我们要插入的值先变成node,再和父节点联系起来
node = BiTreeNode(val)
elif val < node.data:
node.lchild = self.insert(node.lchild,val)
node.lchild.parent = node
elif val > node.data:
node.rchild = self.insert(node.rchild,val)
return node # 一般不考虑相等的情况
def insert_no_sec(self,val): # 不使用递归的方式插入
p = self.root
if not p: # 是否为空,空则创建一个node节点
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild: # 存在左孩子,当前节点跳到左孩子节点去
p = p.lchild
else: # 左孩子不存在,把输入转化为节点放到坐孩子的位置
p.lchild = BiTreeNode(val)
p.lchild.parent = p
return
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
return
else:
return
def query(self,node,val): # 使用递归的查询
if not node: # 终止条件,找不到就返回None
return None
if node.data < val:
return self.query(node.rchild,val)
elif node.data > val:
return self.query(node.lchild,val)
else:
return node
def query_no_rec(self,val): # 不使用递归的查询
p = self.root
while p:
if p.data < val: # 查询值比当前节点值大,就查询当前节点的右孩子
p = p.rchild
elif p.data > val: # 查询值比当前节点值小,就查询当前节点的左孩子
p = p.lchild
else:
return p # 否则当前节点就是目标节点
return None
def pre_order(self,root):
if root:
print(root.data, end=',')
self.pre_order(root.lchild)
self.pre_order(root.rchild)
def in_order(self,root):
if root:
self.in_order(root.lchild)
print(root.data, end=',')
self.in_order(root.rchild)
def post_order(self,root):
if root:
self.post_order(root.lchild)
self.post_order(root.rchild)
print(root.data, end=',')
def __remove__node_1(self,node):
# 情况1:node是叶子节点
if not node.parent:
self.root = None
if node == node.parent.lchild:
node.parent.lchild = None
else:
node.parent.rchild = None
def __remove__node_21(self,node):
# 情况2:node只有一个左孩子
if not node.parent:
self.root = node.lchild
node.lchild.parent = None
elif node == node.parent.lchild:
node.parent.lchild = node.lchild
node.lchild.parent = node.parent
else:
node.parent.rchild = node.lchild
node.lchild.parent = node.parent
def __remove__node__22(self,node):
# 情况3:node只有一个右孩子
if not node.parent:
self.root = node.rchild
elif node == node.parent.lchild:
node.parent.lchild = node.rchild
node.rchild.parent = node.parent
else:
node.parent.rchild = node.rchild
node.rchild.parent = node.parent
def delete(self,val):
if self.root: # 非空树
node = self.query_no_rec(val)
if not node: # 不存在
return False
if not node.lchild and not node.rchild: # 1、叶子节点
self.__remove__node_1(node)
elif not node.rchild: # 2.1只有一个左孩子
self.__remove__node_21(node)
elif not node.lchild: # 2.2只有一个右孩子
self.__remove__node__22(node)
else: # 两个孩子都有
min_node = node.rchild
while min_node.lchild:
min_node = min_node.lchild
node.data = min_node.lchild
node.data = min_node.data
# 删除min_node
if min_node.rchild:
self.__remove__node__22(min_node)
else:
self.__remove__node_1(min_node)
# 主函数
tree = BST([5,6,3,4,2,1,7,8])
# 插入
tree.pre_order(tree.root)
print("")
tree.in_order(tree.root) # 从小到大排序
print("")
tree.post_order(tree.root)
print("")
# 查询
import random
li = list(range(0,500,2))
random.shuffle(li)
tree = BST(li)
print(tree.query_no_rec(102).data)
tree = BST([5,6,3,4,2,1,7,8])
tree.in_order(tree.root)
print("")
tree.delete(3)
tree.in_order(tree.root)
# 输出
5,3,2,1,4,6,7,8,
1,2,3,4,5,6,7,8,
1,2,4,3,8,7,6,5,
102
1,2,3,4,5,6,7,8,
1,2,4,5,6,7,8,
我们通过自行画图可以画出二叉搜索树的结构,如下:
我们从输出中也可以发现,二叉搜索树的中序遍历得到的是从小到大的排序。
除了我们通过输入列表画图可以得到上面的图之外,我们还可以通过输出的三种排序方式,自行画出上图,这又是另外的一个考点。
2、插入操作
如果是已经拥有的树,则根据二叉搜索树的逻辑去进行比较插入:比节点的值大,就往右子树的方向去查找;比节点的值小就往左子树的方向去查找。
def insert(self,node,val): # 使用递归的方式插入
if not node : # 如果要插入的节点node为空,就创建一个节点,内容是val,其实这个是递归的终止条件,在最后的时候把我们要插入的值先变成node,再和父节点联系起来
node = BiTreeNode(val)
elif val < node.data:
node.lchild = self.insert(node.lchild,val)
node.lchild.parent = node
elif val > node.data:
node.rchild = self.insert(node.rchild,val)
return node # 一般不考虑相等的情况
def insert_no_sec(self,val): # 不使用递归的方式插入
p = self.root
if not p: # 是否为空,空则创建一个node节点
self.root = BiTreeNode(val)
return
while True:
if val < p.data:
if p.lchild: # 存在左孩子,当前节点跳到左孩子节点去
p = p.lchild
else: # 左孩子不存在,把输入转化为节点放到坐孩子的位置
p.lchild = BiTreeNode(val)
p.lchild.parent = p
return
elif val > p.data:
if p.rchild:
p = p.rchild
else:
p.rchild = BiTreeNode(val)
p.rchild.parent = p
return
else:
return
3、查询操作
和插入操作一样的道理,当前节点比我们查询的数值大,就往右子树方向搜索;当前节点比我们查询的数值小,我们就往左子树方向搜索。
def query(self,node,val): # 使用递归的查询
if not node: # 终止条件,找不到就返回None
return None
if node.data < val:
return self.query(node.rchild,val)
elif node.data > val:
return self.query(node.lchild,val)
else:
return node
def query_no_rec(self,val): # 不使用递归的查询
p = self.root
while p:
if p.data < val: # 查询值比当前节点值大,就查询当前节点的右孩子
p = p.rchild
elif p.data > val: # 查询值比当前节点值小,就查询当前节点的左孩子
p = p.lchild
else:
return p # 否则当前节点就是目标节点
return None
4、删除操作
二叉搜索树删除情况有三种:
1、删除的节点是叶子节点。我们直接delete;
2、删除的节点不是叶子节点,且只有一个孩子节点。我们把节点的父节点和孩子节点连接到一起,再把该节点delete;
3、删除的节点不是叶子节点,且有两个孩子节点。我们有两种方式去实现,第一种是将该节点delete,再找到左子树中最大的节点,将其进行替换;第二种是将该节点delete,再从右子树找到最小的节点,替换当前节点。(我们这里使用第二种情况)
def __remove__node_1(self,node):
# 情况1:node是叶子节点
if not node.parent: # 判断是否为根节点
self.root = None
if node == node.parent.lchild: # 如果是左孩子就删除为None
node.parent.lchild = None
else: # 如果是右孩子就删除为None
node.parent.rchild = None
def __remove__node_21(self,node):
# 情况2:node只有一个左孩子
if not node.parent: # 判断是否为根节点
self.root = node.lchild
node.lchild.parent = None
elif node == node.parent.lchild: # 如果是父节点的左孩子,将父亲与孩子节点联系到一起
node.parent.lchild = node.lchild
node.lchild.parent = node.parent
else: # 如果是父节点的右孩子,将父亲与孩子节点联系到一起
node.parent.rchild = node.lchild
node.lchild.parent = node.parent
def __remove__node__22(self,node):
# 情况3:node只有一个右孩子 和上一种情况一模一样的思路
if not node.parent:
self.root = node.rchild
elif node == node.parent.lchild:
node.parent.lchild = node.rchild
node.rchild.parent = node.parent
else:
node.parent.rchild = node.rchild
node.rchild.parent = node.parent
def delete(self,val):
if self.root: # 非空树
node = self.query_no_rec(val) # 通过查询方式,找到我们想要的那个点
if not node: # 不存在
return False
if not node.lchild and not node.rchild: # 1、叶子节点
self.__remove__node_1(node)
elif not node.rchild: # 2.1只有一个左孩子
self.__remove__node_21(node)
elif not node.lchild: # 2.2只有一个右孩子
self.__remove__node__22(node)
else: # 两个孩子都有
# 情况4(第三种可能)
min_node = node.rchild # 使用右孩子节点
while min_node.lchild: # 存在左孩子节点的话
min_node = min_node.lchild
node.data = min_node.lchild
node.data = min_node.data
# 删除min_node,因为最小的树就是被删除节点的右孩子节点一直寻找左孩子,到达最底部的左孩子就是右子树最小的值,这个时候一定只有两种情况,1是这个节点是叶子节点,2是这个节点存在一个右孩子。
if min_node.rchild:
self.__remove__node__22(min_node)
else:
self.__remove__node_1(min_node)