二叉搜索树
二叉搜索树定义
对于任意结点,其关键字不小于其左子树的最大关键字,不大于其右子树中的最小关键字。
示例:
二叉搜索树构建 ```c / 31_二叉树——构建二叉查找树/ Status binSerchAddElem_T(BinaryTree& cur, BiTreeNodeElementType elem) { // 方法是向已有二叉搜索树中加入元素,若当前二叉树为空,则构建新的二叉树 // 结点为空时创建结点并设置值,从父节点迭代到左孩子或右孩子时执行 if (!cur) {
cur = (BinaryTree)malloc(sizeof(BinaryTreeNode));
if (!cur)
{
printf("构建失败!");
return ERROR;
}
cur->data = elem;
cur->left = NULL;
cur->right = NULL;
cur->ltag = false;
cur->rtag = false;
return OK;
} // 元素小于当前结点值,创建左结点 if (elem < cur->data) {
if (!binSerchAddElem_T(cur->left, elem))
{
return ERROR;
}
} // 元素大于等于当前结点值,创建右结点 else {
if (!binSerchAddElem_T(cur->right, elem))
{
return ERROR;
}
}
return OK; }
/ 32_二叉树——构建二叉查找树/ void buildBinarySearchTree(BinaryTree& bst, BiTreeNodeElementType arr, int length) { // 批量加元素即可 for (int i = 0; i < length; i++) { binSerchAddElem_T(bst, (arr + i)); } }
- 概念
- 或为空树。
- 左子树不空,左子树上所有结点值均小于根结点值;
- 右子树不空,右子树上所有结点值均大于根结点值。
- 构建过程。
- 顺着二叉搜索树,逐一于当前结点进行比较;
- 若小于当前结点的值,则与其左孩子根结点值比较;
- 否则与右孩子根结点值比较。
- 重复比较过程,直到当前结点为空。创建结点,将元素值赋值到新结点中。
- 二叉搜索树删除单个结点
- 性质不变:删除某个结点后,仍然保持二叉搜索树的结构特性(**中序遍历为非递减序列**)。
- 关键影响因素 => 在BST中**待删除结点的位置**。
- 删除操作的关键:对不同位置结点的删除操作处理,如何**保证**删除后的**性质不变性**。
- 根据对不同情况的处理(主要是**根据后继结点**的处理思路),分为以下几种情况(定义**待删除结点z**,其**左孩子left**, **右孩子right**,**中序后继结点Y**,**Y的右孩子为X**)
- z无左子树,右子树可有可无(00, 01)<br />![BST_Delete_1.svg](https://cdn.nlark.com/yuque/0/2021/svg/21873585/1624244711510-14c663c5-2eff-4e8a-85f1-e233bc1529f2.svg#align=left&display=inline&height=316&margin=%5Bobject%20Object%5D&name=BST_Delete_1.svg&originHeight=316&originWidth=679&size=10398&status=done&style=none&width=679)
- 直接将**右孩子替换Z的位置**
- z有且仅有左孩子(10)<br />![BST_Delete_2.svg](https://cdn.nlark.com/yuque/0/2021/svg/21873585/1624244727466-935f2c6f-af01-4bb9-a2f5-da6ecfdaf3ce.svg#align=left&display=inline&height=326&margin=%5Bobject%20Object%5D&name=BST_Delete_2.svg&originHeight=326&originWidth=766&size=10382&status=done&style=none&width=766)
- 直接将**左孩子替换Z的位置**
- z有两个孩子(11),则需要先找到Z的中序后继结点Y
- Z的右孩子根结点无左子树,则Y == Z->right<br />![BST_Delete_3.svg](https://cdn.nlark.com/yuque/0/2021/svg/21873585/1624244733786-ffc7390b-9277-4208-a497-d6243e6aa6eb.svg#align=left&display=inline&height=476&margin=%5Bobject%20Object%5D&name=BST_Delete_3.svg&originHeight=476&originWidth=1086&size=16675&status=done&style=none&width=1086)
- Y替换Z的位置,并且**左指针**指向Z的左孩子
- Z的右孩子根结点有左子树,则后继Y在其左子树中。<br />![BST_Delete_4.svg](https://cdn.nlark.com/yuque/0/2021/svg/21873585/1624244751232-48430e58-dbda-4ff1-97ac-574873f47b7c.svg#align=left&display=inline&height=696&margin=%5Bobject%20Object%5D&name=BST_Delete_4.svg&originHeight=696&originWidth=1815&size=31934&status=done&style=none&width=1815)
- 首先用**Y的右子树X**的位置替换Y的位置;
- 用结点**Y的右指针指向right**;
- Y的**左指针指向left**,并**替换Z的位置**。
- 注意事项
- 替换位置,即是需要找到其父结点指向该结点的指针,父结点可能通过**左指针或右指针**指向该结点;
- 找Z的中序后继结点Y的操作,关键在于Z的**右子树根结点是否有左子树**。
- 代码实现
```c
/* 33_二叉树——二叉搜索树删除指定结点*/
Status deleteBiSearchElem_T(BinaryTree& bst, BinaryTree& toDel)
{
// 先找到其父结点
BinaryTree parent = parentBiNode_T(bst, toDel);
// 若父结点不存在,则当前即为根结点,直接删除结点并置空
if (!parent)
{
bst = NULL;
delete toDel;
toDel = NULL;
return OK;
}
// 四种情况
// 1. 待删除结点z无左孩子,用右孩子替换
if (!toDel->left)
{
// 当前结点与父节点的关系
if (parent->left == toDel) {
parent->left = toDel->right;
}
else
{
parent->right = toDel->right;
}
delete toDel;
toDel = NULL;
return OK;
}
// 2. 待删除结点z仅有左孩子
else if (toDel->left && !toDel->right)
{
if (parent->left == toDel)
{
parent->left = toDel->left;
}
else
{
parent->right = toDel->left;
}
return OK;
}
// 3. 待删除结点z有两个孩子
else
{
// 待删除结点的后继
BinaryTree post = inorderPost_T(bst, toDel);
// 无左孩子,右子树根结点即是后继
if (post == toDel->right)
{
// 左孩子先替换为待删除结点的左孩子
post->left = toDel->left;
// 将后继替换待删除
if (parent->left == toDel)
{
parent->left = post;
}
else
{
parent->right = post;
}
}
// 否则后继在其z->right的左子树中
else
{
// 后继的父结点p
BinaryTree pParent = parentBiNode_T(bst, post);
if (pParent->left == post)
{
pParent->left = post->right;
}
else
{
pParent->right = post->right;
}
// 将后继替换待删除结点,并连接待删除的右子树
if (parent->left == toDel)
{
parent->left = post;
}
else
{
parent->right = post;
}
post->left = toDel->left;
post->right = toDel->right;
delete toDel;
toDel = NULL;
}
return OK;
}
}
/* 34_二叉树——查找二叉树中某个结点中序后继,找不到返回NULL*/
BinaryTree inorderPost_T(BinaryTree bt, BinaryTree cur)
{
if (!bt || !cur)
{
return NULL;
}
// 1. 有右子树,后继在其右子树中
BinaryTree post = cur->right;
if (post)
{
while (post->left)
{
post = post->left;
}
return post;
}
// 2. 无右子树
BinaryTree parent = NULL;
BinaryTree p = cur;
// 找到当前为父结点左孩子的结点
do
{
parent = parentBiNode_T(bt, p);
p = parent;
} while (parent && parent->left != p);
return parent;
}