二叉搜索树(BST,Binary Search Tree),也称二叉排序树或二叉查找树
本文详细介绍了二叉搜索树的查找,插入,删除以及二叉树搜索树的建立等基本操作:
二叉搜索树定义
二叉搜索树:一棵二叉树,可以为空;如果不为空,满足以下性质:
- 非空左子树的所有结点的值小于其根结点的值。
- 非空右子树的所有结点的值大于其根结点的值。
- 左、右子树都是二叉搜索树。
二叉树的实用性质:对二叉查找树进行中序遍历,遍历的结果是有序的
二叉搜索树的基本操作
1.二叉搜索树的查找
二叉搜索树的查找操作:Find
例如在二叉树搜索树中查找X:
- 查找从根结点开始,如果树为空,返回NULL
- 若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
- 若X小于根结点值,只需在左子树中继续搜索;
- 如果X大于根结点的值,在右子树中进行继续搜索;
- 若两者比较结果是相等,搜索完成,返回指向此结点的指针。
很明显查找操作容易用递归实现,并且是尾递归;但由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数
代码如下:
//在二叉搜索树中查找值为data的点,并放回该结点的地址
template<typename T> BiTree<T>* find_bst(T data,BiTree<T> *bst) {
while (bst) {
if (bst->data == data) //查找成功,返回结点的找到结点的地址
return bst;
else if (bst->data < data)//向右子树中移动,继续查找
bst = bst->right;
else if (bst->data > data)//向左子树中移动,继续查找
bst = bst->left;
}
return NULL; //查找失败,bst为空或者bst中没有data
}
2.二叉搜索树中查找最大和最小元素
由搜索二叉树的性质可知:
- 最大元素一定是在树的最右分枝的端结点上 ,即没有右结点
- 最小元素一定是在树的最左分枝的端结点上 ,即没有左结点
如下图所示:
下面用迭代形式实现查找最小元素:
//在二叉树搜索树中迭代查找值最小的结点,并返回地址
template<typename T>BiTree<T>* find_min(BiTree<T>* bst)
{
if(bst)
while(bst->left != NULL)
bst = bst->left;
return bst;
}
下面用递归形式实现查找最大元素:
//在二叉树搜索树中递归查找值最大的结点,并返回地址
template<typename T>BiTree<T>* find_max(BiTree<T>* bst)
{
if (!bst)return NULL;//如果二叉树为空,直接返回NULl
if (bst->right == NULL)//如果没有右儿子了,那么它就是最大的值
return bst;
else
return find_max(bst->right);//继续递归查找
}
2.二叉搜索树的插入
二叉搜索树的插入是建立二叉搜索树的基本操作
按查找(find)类似的方法找到元素要插入的位置,然后插入即可。插入的示例如下:
由此:插入一定是插入在二叉搜索树的底部的。
递归算法如下:
- 如果bst 为空,则找到x的位置了,插入元素x,生成并返回该节点
- 如果bst不为空
- 如果x < 当前节点的值,则递归插入左子树
- 如果x > 当前节点的值,则递归插入右子树
代码如下:
//在二叉树搜索树bst中插入一个元素data
template<typename T>BiTree<T>* insert_bst(T data, BiTree<T>* &bst) {
if (!bst) {//若原树为空,生成并返回一个结点的二叉搜索树
bst = new BiTree<T>;
bst->data = data;
bst->right = bst->left = NULL;
}
else //开始找要插入元素的位置
{
if (data < bst->data) //递归插入左子树
bst->left = insert_bst(data, bst->left);
if (data > bst->data)//递归插入右子树
bst->right = insert_bst(data, bst->right);
}
return bst;
}
bst一定要传引用啊!我又在这调试了很久!┭┮﹏┭┮
4.二叉树的删除
二叉树中删除一个结点,首先进行查找,查找失败则无法删除:
查找成功,又需分三种情况去删除目标结点:
-
要删除的是叶结点(即无左右孩子):
直接删除,并再修改其父结点指针—置为NULL如下图所示: -
要删除的结点只有一个孩子结点:
将其父结点的指针指向要删除结点的孩子结点:
如下图,删除33,将41的左孩子指针指向35即可
-
要删除的结点有左、右两棵子树:
用另一结点替代被删除结点:
右子树的最小元素(即待删结点的直接后继) 或者 左子树的最大元素(即待删结点的直接前驱)
例如:

- 方法一:用右子树的最小元素替代
- 方法二:用左子树的最大元素替代
具体的细节实现请看下面代码中的注释:
注:下面的代码采用的是右子树最大元素替代的方法,且是用迭代的形式
//在二叉搜索树bst中删除值为data的节点
template<typename T>BiTree<T>* delete_bst(T data, BiTree<T>* &bst) {
BiTree<T> * target,* pre_target, * target_next,* pre_target_next,*temp;
//target是要删除的目标节点,pre_target是待删结点target的双亲节点
//target_next待删结点target的直接后继(target右子树的的最大元素)也就是我们要找的target的替代品
//pre_target_next是结点target_next的双亲节点
//temp临时指针
pre_target = NULL;//这个细节用来出力要删结点就是bst的根节点的情况
target = bst;
/*查找到target*/
while (target != NULL && target->data != data)
{
pre_target = target; //pre_target始终指向target的双亲
if (target->data < data)
target = target->right;
else if (target->data > data)
target = target->left;
}
if (target == NULL)//查找失败
{
cout << "查找失败";
return bst;
}
if (target->left && target->right)//如果目标节点的左右子树都不为空
{
pre_target_next = target;
target_next = target;
while (target_next->left != NULL)//给target_next定位
{
pre_target_next = target_next; //pre_target_next始终指向target_next的双亲
target_next = target->left;
}
target->data = target_next->data;
//targetd_next已经补上去了,所以将其右子树接到其父亲节点上
temp = target_next->right;
//这也要分两种情况
if(pre_target_next == target)
pre_target_next->right = temp;
else
pre_target_next->left = temp;
delete temp;
return bst;
}
else {//第二种情况,待删结点是单支结点
//(第一种情况即待删结点是叶节点也包含在这里)
if (target->left != NULL)
temp = target->left;
else
temp = target->right;
if (pre_target == NULL)//如果要删除的节点就是根节点
{
delete temp;//直接释放掉待删结点的空间就可以了
}
else if(pre_target->right == target)//待删结点是其双亲节点的右孩子
{
pre_target->right = temp; //则把待删结点的孩子接上去就行
}
else if(pre_target->left == target)
{
pre_target->left = temp;
}
delete target;
return bst;
}
}
二叉树基本操作的应用
1.二叉树的建立
基本思路:利用二叉树的插入不断插入
算法就和二叉树的建立类似
代码如下:
//创建一颗二叉树搜索树
template<typename T>BiTree<T>* create_bst() {
T data;
BiTree<T>* bst = NULL;
cin >> data;
while (data != flag)
{
insert_bst(data, bst);
cin >> data;
}
return bst;
}
程序最终测试结果
完整的可调试代码请点击此链接:
二叉搜索树的查找/插入/删除以及二叉树搜索树的建立等基本操作实现的完整代码(可自行调试)