这里我们主要讲二叉排序树的操作:
什么是二叉排序树?
- 或者是一棵空树
- 或者是具有一下性质的二叉树:
a.若它的左子树不空,则左子树上所有结点的值均小于它的根节点的值;
b.若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
c.它的左、右子树也分别为二叉排序树
Tip : 中序(左根右)遍历二叉排序树会得到一个关键字的递增有序序列
二叉排序树的操作——查找
查找步骤:
- 若查找的关键字等于根节点,成功
- 若查找的关键字小于根节点,查找其左子树
- 若查找的关键字大于根节点,查找其右子树
- 在左、右子树上重复操作1~3
算法思想:
- 若二叉排序树为空,则查找失败,返回空指针
- 若二叉排序树非空,将给定的值 key 与根节点的关键字 T->data 进行比较:
a.若 key 等于 T->data,则查找成功,返回根节点地址;
b.若 key 小于 T->data ,则进一步查找其左子树;
c.若 key 大于 T->data,则进一步查找其右子树。
算法描述:
BSTree SearchBST(BSTree T,KeyType key)
{
if((!T) || key == T->data)
return T;
else if (key < T->data)
return SearchBST(T->lchild,key); //在左子树中继续查找
else
return SearchBST(T->rchild,key); //在右子树中继续查找
} // SearchBST
查找的性能分析:
平均查找长度和二叉树的形态有关,即,
最好:log2n(形态匀称,与二分查找的判定树相似)
最坏: (n+1)/2(单支树)
问题:如何提高二叉排序树的查找效率? —— 尽量让二叉树的形状均衡
平衡二叉树(AVL树):
- 所有结点的左、右子树深度之差的绝对值≤ 1
- 左、右子树是平衡二叉树
对于一棵有 n 个结点的AVL树,其高度保持在 O(log2n) 数量级,ASL也保持在 O(log2n) 量级。
树的平衡旋转!
如果在一棵 AVL 树中插入一个新结点,就有可能造成失衡,此时必须重新调整树的结构,
使之恢复平衡。我们称调整平衡过程为平衡旋转。
- LL平衡旋转
- RR平衡旋转
- LR平衡旋转
- RL平衡旋转
LL平衡旋转:
若在A的左子树的左子树上插入结点,使A的平衡因子从1增加至2,需要进行一次顺时针旋转。
(以B为旋转轴)
=========》
RR平衡旋转:
若在A的右子树的右子树上插入结点,使A的平衡因子从-1增加至-2,需要进行一次逆时针旋转。
(以B为旋转轴)
=========》
LR平衡旋转:
若在A的左子树的右子树上插入结点,使A的平衡因子从1增加至2,需要先进行逆时针旋转,再顺时针旋转。
(以插入的结点C为旋转轴)
==①==》==②==》
解释:这里步骤①是根据二叉排序树的特点 A>C>B来做的,因为C>B所以将B作为C的左孩子。
步骤②就是LL平衡转换
RL平衡旋转:
若在A的右子树的左子树上插入结点,使A的平衡因子从-1增加至-2,需要先进行顺时针旋转,再逆时针旋转。
(以插入的结点C为旋转轴)
==①==》==②==》
解释:这里步骤①是根据二叉排序树的特点B>C>A来做的,因为B>C所以将B作为C的右孩子。
步骤②就是RR平衡转换
二叉排序树的操作——插入
插入步骤:
- 若二叉树为空,则插入节点应为根结点
- 若二叉树非空,继续在其左、右子树上查找:
a.树中已经有了要插入的节点,则不再插入
b.树中还没有要插入的节点:查找直至某个叶子结点的左子树或右子树为空为止,
则插入结点应为该叶子结点的左孩子或右孩子
注意:插入的元素一定在叶子结点上
二叉排序树的操作——删除
要求:
1.将因删除结点而断开的二叉链表重新链接起来
2.防止重新链接后的树的高度增加
删除总结:
- 删除叶结点,只需将其双亲结点指向它的指针清零,再释放它即可。
- 被删结点缺右子树,可以拿它的左子女结点顶替它的位置,再释放它。
- 被删结点缺左子树,可以拿它的右子女结点顶替它的位置,再释放它。
- 被删结点左、右子树都存在,可以在它的右子树中寻找中序下的第一个结点(关键码最小),
用它的值填补到被删结点中,再来处理这个结点的删除问题。
实例: