二叉排序树的总结
定义
二叉排序树:或者是一棵空树,或者是一棵满足BST性质的二叉树。
BST性质:
1)如果根结点有左子树,则左子树的所有结点的关键字值都小于根结点的;
2)如果根结点有右子树,则右子树的所有结点的关键字值都大于根结点的;
3)左、右子树都是二叉排序树。
注意
-
可能有人会问,为什么通常称BST为二叉排序树而不是二叉搜索树?
原因是:BST的创建过程的本质是对元素序列的排序。对一棵BST进行中序遍历所得序列一定是递增的。 -
值得注意的是:有人会把折半查找的判定树和二叉排序树搞混。
需要清楚两者之间的联系与区别:
1)联系:容易混淆两者是因为它们的关键字的排序规则是一样的——左子树的关键字小于根,右子树的关键字大于根。而造成这种联系的原因又与他们的区别有关。
2)区别:
判定树由有序查找表构造:判定树是对折半查找过程的描述,而折半查找要求查找表是有序的,因此由于二分查找的本质以及查找表的有序性,就决定了判定树的左子树比根结点小,右子树比根结点大,因此其中序遍历序列是有序的;
二叉排序树在构造过程中对查找表排序:二叉排序树的查找表不一定是有序的,但是因为其创建过程是要符合BST性质的,也就是对查找表进行排序,因此构造出来的一棵二叉排序树的中序遍历序列是有序的。
BST删除操作实现细节剖析
二叉排序树的删除操作要求执行删除操作后仍满足BST性质。
将删除操作分类:
- 删除叶子结点
- 删除无右子树的结点
- 删除无左子树的结点
- 删除有左右子树的结点
我们知道,链式存储结构的删除操作的核心就是找到删除结点的前驱,而对于二叉排序树的删除操作我们利用C++中的引用类型来实现这一目的。
代码实现:
BST结点声明:
typedef struct node
{
KeyType key;
InfoType info;
struct node *lchild;
struct node *rchild;
}BSTNode;
删除操作分2步:
- 查找
bool delete_bst(BSTNode * &bt, KeyType k)
{
if (bt == NULL)
{
return false;
}
else if (k == bt->key)
{
delete1(bt);
return true;
}
else if (k < bt->key)
return delete_bst(bt->lchild, k);
else
return delete_bst(bt->rchild, k);
}
- 删除
void delete1(BSTNode * &p)
{
BSTNode *q;
if (p->rchild == NULL)
{
q = p;
p = p->lchild;
free(q);
}
else if (bt->lchild == NULL)
{
q = p;
p = p->rchild;
free(q);
}
else
delete2(p, p->lchild);
}
void delete2(BSTNode *p, BSTNode * &r)
{
BSTNode *q;
if (r->rchild != NULL)
delete2(p, r->rchild);
else
{
q = r;
p->key = r->key;
p->data = r->data;
r = r->lchild;
free(q);
}
}
剖析:
参数传递只有两种方式,一种是传引用、一种是传值(传指针也属于传值类型)。
引用只是一个别名,因此对引用的操作都是在对原对象操作。
举个例子:
如果我们执行查找函数:
delete_bst(bt->lchild, k);
bool delete_bst(BSTNode * &p, KeyType k)
函数的第一个形参是引用类型,这意味着我们传进去的是bt指针所指结点的lchild成员(lchild成员存放着其后继结点的地址),在函数内对形参p的值的修改本质上是对bt指针所指结点的lchild成员的值的修改。简单的说,当我们调用一个以引用类型作为形参的函数的时候,我们实质是让函数给我们传进去的实参创建了一个别名,在函数内对别名的操作都会作用在实参上。
因此,如果形参p的key等于k,那么执行删除操作(注意此时形参p只是实参bt->child的别名),即执行删除函数:
delete1函数的声明:
void delete1(BSTNode * &p)
delete1(p); //p是bt->lchild的别名而已
如果删除情况刚好是p->rchild为空的情况,那么函数内会执行语句块:
BSTNode *q;
if (p->rchild == NULL)
{
q = p;
p = p->lchild;
free(q);
}
为了直观,我们直接用最开始的实参bt->lchild替换别名p,则代码是这样的:
BSTNode *q;
if (bt->lchild->rchild == NULL)
{
q = bt->lchild;
bt->lchild = bt->lchild->lchild;
free(q);
}
内存图: