1 二叉排序树简介
二叉排序树(也称二叉查找树)。或者是一棵空树,或者是具有下列性质的二叉树:
性质:左子树结点值 < 根结点值 < 右子树结点值
所以对二叉排序树进行中序遍历(左根右),可以得到一个递增的序列。
2 二叉排序树的各种各种操作
2.1 二叉排序树的查找
从根结点开始,沿某个分支逐层向下比较的过程。先将待查找的值和根结点进行比较,
若相等,查找成功;若不等,如果小于根结点的值,则在根结点的左子树上查找,
否则在根结点的右子树上查找。
//二叉排序树结点
typedef struct BSTNode{
int key;
struct BSTNode *lchild,*rchild;
}BSTNode,*BSTree;
非递归代码:
//在二叉排序树中查找结点值为key的结点
BSTNode *BST_Search(BSTree T,int key){
while(T!=NULL&&key!=T->key){
if(key<T->key) //小于,则在左子树上进行查找
T=T->lchild;
else
T=T->rchild;
}
return T;
}
递归代码:
//查找的递归实现,当然,递归的效率肯定是不如上面的非递归的
BSTNode *BSTSearch(BSTree T,int key){
if(T==NULL)
return NULL;
if(key==T->key)
return T;
else if(key<T->key)
return BSTSearch(T->lchild,key);
else
return BSTSearch(T->rchild,key);
}
2.2 二叉排序树的插入
需要注意的是,二叉排序树不能插入重复的结点!!新插入的结点一定是叶子结点。
直接看代码
//二叉排序树的插入
int BST_Insert(BSTree &T,int k){ //注意这里参数是引用类型的
if(T==NULL){ //如果树为空,那么直接插入即可
T=(BSTree)malloc(sizeof(BSTNode));
T->key=k;
T->lchild=T->rchild=NULL;
return 1; //插入成功,返回1
}
else if(key==T->key){ //如果树中存在相同关键字的结点,插入失败
return 0;
}else if(key<T->key){
return BST_Insert(T->lchild,key);
}else{
return BST_Insert(T->rchild,key);
}
}
最坏空间复杂度为O(h) (h为树的高度)
2.3 二叉排序树的构造
二叉排序树的构造本质就是从一棵空树出发,依次插入结点。需要注意的是,插入的元素顺序不一样可能会构造不一样的二叉排序树。也可能会构造一样的二叉排序树
void Create_BST(BSTree &T,int str[],int n){ //注意这里参数是引用类型的。str[]是存储关键字的数组
T=NULL; //初始时T为空树
int i=0;
while(i<n){
BST_Insert(T,str[i]); //依次将每个关键字插入到二叉排序树中
i++;
}
}
2.4 二叉排序树的删除
在二叉排序树删除一个结点时,不能把以这个结点为根的子树上的结点都删除,必须先把被删除结点从存储二叉排序树的链表上删除,将因删除结点而断开的二叉链表重新链接起来,同时确保二叉排序树的性质不会丢失。
分为以下三种情况:
- 若被删除的是叶结点,则直接删除
- 若结点z只有一棵左子树或右子树,则让z的子树成为z的父结点的子树,替代z的位置。(说白了就是用z的子树去替代z的位置)
- 若结点z有左、右两棵子树,则令z的直接后继(或直接前驱)替代z,然后从二叉排序树中删除去这个直接后继(或直接前驱),这样就转换成了第一种或第二种情况。
对于第三种情况:
- z的直接后继一定是z的右子树的最左下结点。(为什么呢?因为二叉排序树的性质其实就相当于进行中序遍历之后是一个递增序列,而中序遍历:【 左根((左根右)根右))】
- z的直接前驱一定是z的左子树的最右下结点
2.5 二叉排序树的查找效率分析
主要取决于树的高度。
若二叉排序树的左、右子树的高度之差的绝对值不超过1,则这样的二叉排序树称为平衡二叉树,它的平均查找长度为O(log2n)。
若二叉排序树是一个只有右(左)孩子的单支树(也就是输入序列是一个有序序列),当查找一个不存在的关键字值或最后一个结点的关键字值时,需要n次比较。
总结:平均查找长度O(log2n),最坏为O(n)
2.5.1 二叉排序树和二分查找的比较
从查找过程看,两者相似
从平均时间性能看,两者差不多
但二叉查找的判断树是唯一的,二叉排序树的查找不唯一。
就维护表的有序性而言,二叉排序树无须移动结点,只需修改指针即可完成插入和删除操作,平均执行时间为O(log2n)。二分查找的对象是有序顺序表,若有插入和删除结点的操作,所花时间为O(n)。
总结:当有序表是静态查找表时,宜用顺序表作为其存储结构,采用二分查找实现其查找操作。
当有序表是动态查找表时,则应选择二叉排序树作为其逻辑结构。
2.5.2 查找成功的平均查找长度ASL
ASL:Average Search Length
根结点视为第一层,以此类推,下面的分别为第二层,第三层。。。
ASL=(1*1+2*2+4*3+1*4)/8 = 2.625
2.5.3 查找失败的平均查找长度ASL
ASL=(7*3 + 2*4 )/ 9 = 3.22
3、二叉排序树的相关题目
3.1 构造一棵具有n个结点的二叉排序树时,最理想情况下的深度为(log2(n+1)向上取整 )
解析:最理想情况就是从第一层到倒数第二层为满二叉树。