二叉查找树Python实现
原理介绍
每一个节点都有存放数据的部分和左右指针三个部分构成,整个节点连接起来组成一个二叉树,该二叉树的每个节点的左侧节点得数据都比自己的数据小,右侧节点的数据都比自己的数据大。
因此在查找的时候可以有很高的效率,在找一个节点的时候就从根节点开始,不停的比较大小,大了就往右子树走,小了就往左子树走,最终就找到了。
主要是删除节点比较麻烦
在删除这个30节点的时候,为了维持二叉查找树左小右大的特性,需要有一个麻烦的操作。
需要找到30的 右子树 中的 最左下角的节点(就是一直往左侧走走到尽头)
让最左下节点替换删除位置
这个最左下位置肯定没有左孩子了,因为它已经是最左下节点了,如果它有右孩子,就让他的右孩子上移动替换原来这个图中32的位置。
代码说明
对于一个叉查找树数据元素类型是int类型,实现了增加数据、查找数据、以及删除数据
如果插入了重复的数据,相当于没有插入新数据。
代码的使用详见代码最下面main函数的部分
直接print(二叉查找树对象)
打印出来的是有序排列的结果,调用该对象的print方法才是打印树的结构,打印出来的树的结构中,None 表示成了‘x’。整体的样子可以想象成文章大纲缩进图
待更新
将二叉查找树做成平衡二叉树。也就是插入元素和删除元素导致树的形状不平衡的时候,树应该需要自己调整节点来达到平衡。
平衡是什么意思?平衡就是每一个节点,左子树和右子树的高度差不超过1
源代码
"""
平衡二叉树实现
目前只做了二叉查找树,还没做平衡效果
2021年11月20日
by littlefean
"""
from typing import List
class AvlTree:
"""
查找二叉树,左小右大
"""
class TreeNode:
"""节点"""
def __init__(self, val):
self.val = val
self.leftNode = None
self.rightNode = None
def __str__(self):
return f"<{self.val}>"
def __init__(self):
self.root = None
...
def contain(self, n: int) -> bool:
"""查找一个数字,是不是在树中"""
res = [False]
def find(node: AvlTree.TreeNode):
if node is None:
res[0] = False
return
if n > node.val:
find(node.rightNode)
elif n < node.val:
find(node.leftNode)
else:
res[0] = True
find(self.root)
return res[0]
def add(self, n: int):
"""往平衡二叉树中添加一个数,如果添加的数字在该二叉树中已经出现,就不会再添加此数字"""
def add_dfs(node):
if n > node.val:
# 大,去找右子树
if node.rightNode is None:
node.rightNode = self.TreeNode(n)
else:
add_dfs(node.rightNode)
elif n == node.val:
# print("插入失败")
pass
else:
# 小,去找左子树
if node.leftNode is None:
node.leftNode = self.TreeNode(n)
else:
add_dfs(node.leftNode)
if self.root is None:
self.root = self.TreeNode(n)
else:
add_dfs(self.root)
def addNums(self, arr: List[int]):
"""批量添加"""
for n in arr:
self.add(n)
def delNum(self, n: int):
"""传入一个数值,寻找这个数值并删除节点"""
# 第一个存放被删除节点,第二个存放它的父节点
res = [self.TreeNode(None), self.TreeNode(None), False]
# 先找到这个节点
def find(node: AvlTree.TreeNode):
if node is None:
return
if n > node.val:
res[1] = node
res[2] = False
find(node.rightNode)
elif n < node.val:
res[1] = node
res[2] = True
find(node.leftNode)
else:
res[0] = node
find(self.root)
resNode, resFatherNode, isLeft = res
if resNode.val is None:
# 说明没有找到
pass
elif resNode.leftNode is None and resNode.rightNode is None:
# 要删除的节点是叶子节点
if isLeft:
resFatherNode.leftNode = None
else:
resFatherNode.rightNode = None
del resNode
elif resNode.leftNode is not None and resNode.rightNode is None:
# 有左子树,但是没有右
if isLeft:
resFatherNode.leftNode = resNode.leftNode
else:
resFatherNode.rightNode = resNode.leftNode
elif resNode.leftNode is None and resNode.rightNode is not None:
# 有右子树,但是没有左子树
if isLeft:
resFatherNode.leftNode = resNode.rightNode
else:
resFatherNode.rightNode = resNode.rightNode
else:
# 左右子树都有
# 用删除节点的右子树的最左下节点替换被删除位置,如果最左下节点有右子树,让右子树接上去
leftCornerNodePar = [resNode.rightNode, resNode]
def dfs(node):
if node.leftNode is not None:
leftCornerNodePar[1] = node
dfs(node.leftNode)
else:
leftCornerNodePar[0] = node
return
dfs(resNode.rightNode)
if leftCornerNodePar[0].rightNode is None:
leftCornerNodePar[0].leftNode = resNode.leftNode
leftCornerNodePar[0].rightNode = resNode.rightNode
if isLeft:
resFatherNode.leftNode = leftCornerNodePar[0]
else:
resFatherNode.rightNode = leftCornerNodePar[0]
leftCornerNodePar[1].leftNode = None
else:
# 如果最左下节点有右子树
leftCornerNodePar[0].leftNode = resNode.leftNode
leftCornerNodePar[1].leftNode = leftCornerNodePar[0].rightNode
leftCornerNodePar[0].rightNode = resNode.rightNode
if isLeft:
resFatherNode.leftNode = leftCornerNodePar[0]
else:
resFatherNode.rightNode = leftCornerNodePar[0]
def __str__(self):
res = []
def dfs(node):
if node is None:
return
dfs(node.leftNode)
res.append(str(node.val))
dfs(node.rightNode)
dfs(self.root)
return ",".join(res)
def print(self):
def dfs(node, tabNum):
if node is None:
print(" " * tabNum + "x")
else:
print(" " * tabNum + str(node.val))
dfs(node.leftNode, tabNum + 1)
dfs(node.rightNode, tabNum + 1)
dfs(self.root, 0)
def main():
a = AvlTree()
a.addNums([20, 10, 30, 25, 35, 32, 33])
a.print()
a.delNum(30)
a.print()
b = AvlTree()
b.addNums([20, 10, 30, 25, 35, 32])
b.print()
b.delNum(30)
b.print()
...
if __name__ == '__main__':
main()