因为看java内部库实现的时候,没看出来一些典型的算法,补补课相当于
别问为什么用python实现
二叉查找树
Binary Search Tree => BST
二叉查找树根节点的值大于其左子树中任意一个节点的值,小于其右子树中任意一节点的值,且该规则适用于树中的每一个节点。
二叉查找树的查询效率介于O(log n)~O(n)之间,理想的排序情况下查询效率为O(log n),极端情况下BST就是一个链表结构(如下图),此时元素查找的效率相等于链表查询O(n)。
BST 增删改查
不考虑其他情况(BST的查找性能或其他时),仅考虑增加元素时的增删改查
增
- 空树的情况:当BST为空时,新插入的节点成为根节点。
- 插入到左子树或右子树的叶子节点位置:例如,当前节点有左子树为空,且新键小于当前节点键,则新节点作为左子节点插入。
- 递归遍历到适当的位置:在非叶子节点的情况下,需要继续向下遍历左或右子树,直到找到空的位置。
- 重复键的处理:可能需要返回错误或者忽略,或者根据需求处理。
删
- 删除叶子节点:这是最简单的情况,直接删除即可,不影响其他节点。
- 删除有一个子节点的节点:需要将父节点的指针指向被删除节点的子节点,保持BST的有序性。
- 删除有两个子节点的节点:这种情况最复杂,需要找到合适的替换节点(通常是左子树的最大值或右子树的最小值),然后递归删除替换节点,以避免破坏BST的结构。
- 删除节点不存在:返回原BST即可。
改
-
修改后的键值与原键值相同: 这种情况下,实际上不需要做任何修改,因为键值没有变化。但如果是修改与键关联的数据(如值或附加信息),则可以直接更新数据部分,不影响树的结构。
-
修改后的键值不改变其在树中的位置: 新键值仍然位于原节点的父节点和子节点之间,即新键值仍然满足原父节点的左子树或右子树的条件,以及其子节点的条件。 只要满足:
-
大于左子树的最大键值且小于右子树的最小键值
-
父节点约束(若存在父节点):
-
若当前节点是父节点的左子节点,新键值 < 父节点键值。
-
若当前节点是父节点的右子节点,新键值 > 父节点键值。
-
此时,无需调整树结构。
-
-
修改后的键值破坏了BST的有序性,需要调整树结构: 这是更复杂的情况。例如,将某个节点的键值修改为比其左子树的某个节点小,或比其右子树的某个节点大,导致无法维持BST的性质。 此时,必须删除原节点,然后插入新的键值,以确保整个树的有序性。删和增参考之前的描述操作
查
就是简单的对比查找即可,不存在返回空
平衡二叉查找树
在BST的模式下,如果按照默认插入方式,极端情况下可能会造成BST退化为链表的状态
所以,需要解决如下问题:
- 我何时知道我的 BST 变成了 “不好” 的状态;(根据什么规则可以确定我的BST退化了?)
- 我如何调整我的 BST ,使其达到可接受的平衡状态
为了防止BST的退化,或找到最适合业务,最快的查询树是需要满足什么条件?什么样的树是平衡的,不是退化的?于是AVL被提出。
Balanced Binary Search Tree => AVL
AVL 是大学教授 G.M. Adelson-Velsky 和 E.M. Landis 名称的缩写,他们提出的平衡二叉树的概念,为了纪念他们,将 平衡二叉树 称为 AVL树。
AVL树本质上是一颗二叉查找树,但是它又具有以下特点:
- 它是一棵空树 or 它的左右两个子树的高度差的绝对值不超过1,
- 左右两个子树也都是一棵AVL树。
AVL树判断是否平衡的标志,引入了平衡因子的概念(Balance Factor,简写为BF)
平衡因子(BF):结点的左子树的深度减去右子树的深度。
即: 结点的平衡因子 = 左子树的高度 - 右子树的高度 。
图示:
分别计算当前节点的左最高高度和右最高高度,差为BF
在 AVL树中,所有节点的平衡因子都必须满足: |bf|<=1;
AVL 增删改查
AVL 的查操作与BST一样,改操作可能有直接修改键值并局部调整的算法,但工业级实现(如标准库)普遍采用删增分解策略,所以我们的目光放在增和删上
增
https://blog.csdn.net/xiaojin21cen/article/details/97602146
没错,不重复造轮子,这个文章写的很好了已经
贴个总结,纯黑是插入的节点
删
按照BST删的方式进行删除后,从子节点开始计算BF,重新进行左旋右旋操作即可
代码
BST
带可视化输出:
|
|
|
|
AVL
同样带可视化输出
|
|
|
|