二叉排序树
定义:
- 二叉排序树可以是一颗空树,或者是满足下列性质的二叉树
- 若左子树不为空,则左子树上所有结点值均小于它根结点的值
- 若左右子树不为空,则右子树上所有结点值均大于它根结点的值
- 左右子树也均为二叉排序树
特点:当通过中序遍历一颗二叉排序树时,得到的时一个递增序列
查找
算法思想
- 如果二叉排序树空,则查找失败,返回空指针
- 若不为空,则将给定数值key与根节点的关键字T->data.key比较:
- 若key==T->data.key,则查找成功,返回根节点地址
- 若key>T->data.key,则递归查找右子树
- 若key<T->data.key,则递归查找左子树
例如上图中,我们查找89:
- 由于二叉排序树不为空,则先于根节点的关键字43比较,发现比43大,因此进入右子树
- 右子树不为空,与其内的78比较,发现比78大,因此继续进入右子树
- 右子树不为空,与其内的89比较,发现相等,因此查找成功,返回该结点地址
平均查找长度 ASL=(∑对应层数*对应层数内的结点数)/结点总数
例如上图的 ASL=(1*1+2*2+3*3+4*1+5*1)/8=2.875
插入
算法思想:
- 如果二叉排序树为空,则带插入结点s作为根节点插入到空树中
- 若二叉排序树非空,则将值key与根节点的关键字T->data.key进行比较:
- 若key>T->data.key,则将s插入左子树(插入后继续比较,直至成为终端结点)
- 若key<T->data.key,则将s插入右子树(插入后继续比较,直至成为终端结点)
创建
在插入的基础上进行二叉排序树的创建
要明白一点,对于同一堆数值,不同的输入顺序会得到不同的二叉排序树
例如,对于(43,67,23,89,56,18,34)
打乱顺序,对于(23,56,34,43,18,89,67)
删除
关键:删除结点以后仍要保持二叉排序树的性质
删除的是叶子结点:直接删除结点即可
删除的结点只有左子树(右子树):直接用左孩子(右孩子)覆盖掉原结点即可
删除的结点既有左子树又有右子树
- 用该结点的中序遍历里的前驱结点代替他,对于他的前驱结点进行上述相同的操作
- 或者用该节点的中序遍历中的后继结点代替他,对于他的后继结点进行上述相同的操作
例如,
要删除结点50
中序遍历序列为:20、30、32、35、40、45、50、80、85、88、90
- 由于结点50既有左子树又有右子树,先用其前驱40代替结点50
- 对于结点40,由于其也既有左子树又有右子树,则再用其前驱35代替结点40
- 对于结点35,由于其只有左子树,则直接用左孩子替代即可
删后结果为,
平衡二叉树(AVL树)
定义:
- AVL树可以是空树,或者是具有以下性质的二叉树
- | 左子树depth - 右子树depth | ≤ 1
- 左子树和右子树也是AVL树
- 平衡因子BF:左子树与右子树的深度之差【AVL树中的BF只可能是-1、0、1】
平衡调整:
失衡:平衡因子不再是-1、0、1
例如,在插入60后,结点53的平衡因子变成-2
调整原则 :
- 不止一个失衡结点时,找到失衡的最小平衡因子的结点
- 降低高度,保持二叉排序树性质
LL型 【插入结点为左子树的左子树】
方法:顺时针旋转法
- 左子树B 带着 左子树B 的 左子树C 上移【左子树B的右子树D断裂】
- 原根节点A 成为 原左子树B 的右孩子
- 原左子树B 的 右子树D 成为 原根节点A 的左子树
RR型【插入结点为右子树的右子树】
方法:逆时针旋转法
- 右子树B带着右子树B的右子树C上移【右子树B的左子树D断裂】
- 原根节点A 成为 原右子树B的左孩子
- 原右子树B 的 左子树D 成为 原根节点A 的右子树
LR型【插入结点为左子树的右子树】
方法:先逆后顺法
- 先对左子树B和左子树B的右子树C进行一次逆旋转,B成为C的左孩子【此时成为LL型】
- 进行LL型的调整
RL型【插入结点为右子树的左子树】
方法:先顺后逆法
- 先对右子树B和右子树B的左子树C进行一次顺旋转,B成为C的右孩子【此时成为RR型】
- 进行RR型的调整
试炼:构建序列为(16、3、7、11、9、26、18、14、15)的AVL树
结果: