二叉排序树
1、引言
当表插入、删除操作频繁时,为维护表的有序性,需要移动表中很多记录。
改用动态查找表————几种特殊的树
表结构在查找过程中动态生成
对于给定值key,若表中存在,则成功返回;
否则,插入关键字等于key的记录
二叉排序树 平衡二叉树 红黑树 B-树 B+树 键树
2、定义
二叉排序树(Binary Sort Tree)
又称为二叉搜索树,二叉查找树
二叉排序树或是空树,或是满足如下性质的二叉树:
- 若其左子树非空,则左子树上所有结点的值均小于根结点的值;
- 若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
- 其左右子树本身又各是一颗二叉排序树
- 特点:中序遍历序列为递增序列3、中序遍历二叉排序树规律(二叉排序树的性质)
中序遍历非空的二叉排序树所得到的数据元素序列是一个按关键字排列的递增有序序列
4、二叉排序树的操作——查找
- 若查找的关键字等于根结点,成功
- 否则
- 若小于根结点,查其左子树
- 若大于根结点,查其右子树
- 在左右子树上的操作类似
5、二叉排序树的存储结构
typedef struct{
KeyType key; //关键字项
InfoType otherinfo; //其他数据域
}ElemType;
typedef struct BSTNode{
ElemType data; //数据域
struct BSTNode *lchild, *rchild ; //左右孩子指针
}BSTNode,*BSTree;
BSTree T; //定义二叉排序树T
6、二叉排序树的递归查找
算法思想
-
若二叉排序树为空,则查找失败,返回空指针
-
若二叉排序树非空,将给定值key与根结点的关键字
T–>data.key进行比较:
- 若key等于T->data.key,则查找成功,返回根结点地址;
- 若key小于T->data.key, 则进一步查找左子树;
- 若key大于T->data.key, 则进一步查找右子树;
算法描述
BSTree SearchBST(BSTree T,KeyType key){
if((!T) || key == T-->data.key){
return T;
}else if(key< T->data.key){
return SearchBST(T->lchild,key);//在左子树中继续查找
}else {
return SearchBST(T->rchild,key);//在右子树中继续查找
}
}//SearchBST
7、二叉排序树的查找分析
二叉排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从跟到该结点的路径。
比较的关键字次数=此节点所在层次数
最多的比较次数=树的深度
二叉排序树的平均查找长度
树的高度越高,平均查找长度越长(树的高度不超过 lg2(n+1))
含有n个结点的二叉排序树的平均查找长度和树的形态有关
最好的情况:ASL=log2(n+1) -1;
树的深度为:log2 n次 (去整数)+ 1
与这把查找中的判定树相同
最坏的情况:插入的n个元素从一开始就有序
-----变成单支树的形态!
此时树的深度为n,ASL = (n+1)/ 2
查找效率与顺序查找情况相同:O(n)
问题:如何提高形态不均与的二叉排序树的查找效率?
解决办法:做“平衡化”处理,即尽量让二叉树的形状均衡!
8、二叉排序树的操作
8.1、插入
方法:
- 若二叉树为空,则插入结点作为根结点插入到空树中
- 否则,继续再其左、右子树上查找
- 树中已有,不在插入
- 树中没有
- 查找直至某个叶子结点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或者右孩子
插入的元素一定在叶节点上
8.2、生成
从空树出发,经过一系列的查找,插入操作之后,可生成一颗二叉排序树。
例如:设查找的关键字序列为 {45,24,53,45,12,24,90 }
45
24 53
12 90
总结:
- 一个无序序列可通过构造二叉排序树而变成一个有序序列(中序排序),构造树的过程就是对无需序列进行排序的过程
- 插入的结点均为叶子结点,故无需移动其他结点。相当于在有序序列上插入记录二无需移动其他记录
- 关键字的输入顺序不同,会建立不同的二叉排序树
8.3、删除
从二叉排序树中删除一个结点,不能把以该结点为根的子树都删去,只能删掉该结点,并且还应该保证删除后所得的二叉树仍然满足二叉排序树的性质不变
由于中序遍历二叉排序树可以得到一个递增有序的序列。那么在二叉排序树中删去一个结点相当于删去有序序列中的一个结点
- 将因删除结点而断开的二叉链表重新连接起来
- 防止重新连接后树的高度增加(高度决定了二叉排序树的查找效率)
-
被删除的结点是叶子结点:直接删除该结点,修改其双亲结点中相应指针域的值改为“空”
-
被删除的结点只有左子树或者只有右子树,用其左子树或者右子树替换它(结点替换)
其双亲结点的相应指针域的值改为:“指向被删除结点的左子树或右子树”
-
被删除的结点既有左子树,也有右子树
50
30 80
20 40 90
35 85
32 88
被删除关键字 = 50
- 以其中序前驱值替换之,(值替换)然后在删除该前驱结点。
前驱结点是左子树中最大的结点
- 也可以用其后继替换之,然后在删除该后继结点,
后继是左子树中最小的结点
- 以其中序前驱值替换之,(值替换)然后在删除该前驱结点。
9、二叉排序树的优点
在二叉排序树中插入新结点时,新结点总是以叶子结点的形式被添加到树中。
新结点的插入对已有结点的位置与树的原有形态没有影响。
如果把二叉排序树看成一个有序序列,即相当于在一个有序序列中插入新元素,不会影响已有元素。
对比:在进行折半查找的有序序列中,插入新元素会导致插入位置之后的所有元素向后移动。
课本重点
1、二叉排序树的性质
- 若根结点的左子树非空,则左子树上的所有结点关键字均小于根结点关键字
- 若根结点的右子树非空,则右子树上的所有结点关键字均大于根结点关键字
- 根结点的左右子树本身又各是一颗二叉排序树
- 按中序遍历该树所得到的中序序列是一个递增有序序列
也就是说二叉排序树是在二叉树基础上增加了结点值的约束
补充:
每一个数据都是从根结点开始向下比较大小
该数据比较玩后,就是准确周到插入位置,然后一顿作为叶子插入
2、重点
高度越小的二叉排序树查找效率越高
关键字比较的次数不超过树的高度
二叉排序树的查找
二叉排序树上进行的平均查找长度和二叉排序树的形态有关
- 最好情况 (n+1)/2
- 最坏情况 以2为底n的对数,树的形态比较均匀,最终得到的是一颗形态与折半查找的判定树相似的BST
总结:就平均时间性能而已,二叉排序树上的查找和折半查找差不多,但是就维护表的有序性来说,二叉排序树更有效,因为无需移动元素,只需要修改指针即可完成结点的插入和删除操作
最大结点问题
一颗二叉排序树中的最大结点为根结点的最右下结点,最小结点为根结点的最左下结点