概念
二叉排序树 $Binary$ $Sort$ $Tree(BST)$, 也称二叉查找树
graph LR
X(特点)
X-->A(若左子树非空, 则左子树上所有结点值均小于其根结点值)
X-->B(若右子树非空, 则右子树上所有结点值均大于其根结点值)
X-->C(其左、右子树也是二叉排序树)
X-->D(没有键值相等结点)
- 示例
graph TB
N45((45))
N45-->N11((11))
N11-->N9((9))
N11-->N23((23))
N45-->N68((68))
N68-->N76((76))
对该树进行中序遍历($LDR$)会得到一个递增有序序列: $9, 11, 23, 45, 50, 68, 76$
操作
查找
graph LR
X(规则)
X-->A(非空时查找根结点, 若与查找值相等则查找成功)
X-->B(若与查找值不相等)
B --> B1(查找值小于根结点值, 查找左子树)
B --> B2(查找值大于根结点值, 查找右子树)
X-->C(到叶节点仍未找到, 查找失败)
- 查找 $23$
graph TB
N45(((45)))
N45-->N11(((11)))
N11-->N9((9))
N11-->N23(((23)))
N45-->N68((68))
N68-->N76((76))
因为 $23 < 45$, 则查找左子树
因为 $23 > 11$, 则查找右子树, $23 = 23$, 查找成功
- 查找 $47$
graph TB
N45(((45)))
N45-->N11((11))
N11-->N9((9))
N11-->N23((23))
N45-->N68(((68)))
N68-->N76((76))
因为 $47 > 45$, 则查找右子树
因为 $47 < 68$, 则查找左子树, 左子树为空, 查找失败
递归写法
template<typename T>
struct BSTNode {
T value;
BSTNode<T> *leftSon;
BSTNode<T> *rightSon;
BSTNode(T value, BSTNode<T> *leftSon, BSTNode<T> *rightSon {
this->value = value;
this->leftSon = leftSon;
this->rightSon = rightSon;
}
};
template<typename T>
BSTNode<T> *Search(BSTNode<T> *root, const T value) {
if (root == nullptr) {
return nullptr;
}
if (root->value == value) {
return root;
}
if (root->value > value) {
return Search(root->lchind, value);
}
if (root->value < value) {
return Search(root->rchind, value);
}
}
非递归写法
template<typename T>
BSTNode<T> *Search(BSTNode<T> *root, const T value) {
while(root != nullptr) {
if (root->value == value) {
return root;
}
if (root->value > value) {
root = root->lchind;
}
if (root->value < value) {
root = root->rchind;
}
}
return nullptr;
}
插入
graph LR
X(规则)
X --> A(若二叉排序树为空, 直接插入结点)
X --> B(若二叉排序树非空)
B --> B1(插入值小于根结点时, 插入左子树)
B --> B2(插入值大于根结点时, 插入右子树)
X --> C(若插入值等于根结点时不进行插入)
X --> D(新插入结点总是叶子结点)
- 插入 $15$
因为 $15 < 45$, 选择左子树,
因为 $15 > 11$, 选择右子树,
因为 $15 < 23$, 选择左子树, 左子树为空, 插入
graph TB
N45(((45)))
N45-->N11(((11)))
N11-->N9((9))
N11-->N23(((23)))
N23-->N15(((15)))
N45-->N68((68))
N68-->N76((76))
// 返回构建完成后根节点
template<class T>
BSTNode<T> *AddNode(BSTNode<T> *root, const T value) {
// 节点为空, 说明是叶子节点
if(root == nullptr){
root = new BSTNode<T>(value, nullptr, nullptr);
return root;
}
// 值小于根结点时, 插入根节点左子树
if(root->value > value) {
root->leftSon = AddNode(root->leftSo, value);
}
// 值大于根结点时, 插入根节点右子树
if(root->value < value) {
root->rightSon = AddNode(root->rightSon, value);
}
return root;
}
构建
根节点为空, 进行插入操作
template<class T>
void Init(BSTNode<T> *root, vector<T> &v) {
for(int i = 0, size = v.size(); i < size; i++) {
root = AddNode(root, v[i]);
}
}
删除
graph LR
X(规则)
X --> A(叶子结点直接删除)
X --> B(若被删除结点 x 只有一个子树)
B --> B1(让x子树成为 x 父结点的子树, 代替 x 结点)
X --> C(若被删除结点x有两个子树)
C --> C1(让x右子树中最小节点 y 代替 x, 并删除y)
- 删除 $9$ 节点
$9$ 节点是叶子节点, 直接删除
graph TB
N45((45))
N45 --> N11((11))
N11 -.-> N9(((9)))
N11 --> N23((23))
N45 --> N68((68))
N68 --> N76((76))
- 删除 $68$
$68$ 节点只有一个子树, 让 $68$ 子树 $76$, 成为 $68$ 父节点 $45$ 的子树
graph TB
N45((45))
N45 --> N11((11))
N11 --> N9((9))
N11 --> N23((23))
N45 -.-> N68((68))
N68 -.-> N76((76))
N45 --> N76
graph TB
N45((45))
N45 --> N11((11))
N11 --> N9((9))
N11 --> N23((23))
N45 --> N76((76))
- 删除 $11$
$11$ 右子树中最小节点为 $23$, 让 $23$ 代替 $11$
graph TB
N45((45))
N45-->N11((11))
N11-->N9((9))
N11-.->N23((23))
N45-->N68((68))
N68-->N76((76))
N23-->N11
graph TB
N45((45))
N45-->N23((23))
N23-->N9((9))
N45-->N68((68))
N68-->N76((76))
template<class T>
void DelNode(BSTNode<T> *root, const T value){
if (root == nullptr) {
return;
}
// p为待删除节点, fp为其父节点
BSTNode<T> *p = root;
BSTNode<T> *fp = root;
while (p->value != value) {
fp = p;
if (p->value > value) {
// 查找左子树
p = p->leftSon;
}
if (p->value < value) {
// 查找右子树
p = p->rightSon;
}
}
// 情况1, p为叶子节点, 直接删
if (p->leftSon == nullptr && p->rightSon == nullptr) {
if (fp->leftSon != nullptr) {
fp->leftSon = nullptr;
}
if (fp->rightSon != nullptr) {
fp->rightSon = nullptr;
}
delete(p);
p = nullptr;
return;
}
// 情况2, p左子树为空, 重接右子树
if (p->leftSon == nullptr) {
p->value = p->rightSon->value;
p->rightSon = nullptr;
delete(p);
p = nullptr;
return;
}
// 情况3, p右子树为空, 重接左子树
if (p->rightSon == nullptr) {
p->value = p->leftSon->value;
delete(p->leftSon);
p->leftSon = nullptr;
return;
}
// 情况4, p左右子树均不为空时, 需要找p右子树中最小节点(最左节点)q
BSTNode<T> *q = p->rightSon;
// fq为q父节点
BSTNode<T> *fq = q;
// 循环查找左节点, 就会找到最小值
while(q->leftSon != nullptr) {
fq = q;
q = q->leftSon;
}
fq->leftSon = nullptr;
// 用最小值节点代替欲删除节点
p->value = q->value;
delete(q);
q = nullptr;
return;
}
遍历
// 中序遍历
template<class T>
void OutputBST(BSTNode<T> *root) {
if (root->leftSon != nullptr) {
OutputBST(root->leftSon);
}
std::cout << root->value << std::endl;
if (root->rightSon != nullptr) {
OutputBST(root->rightSon);
}
}
下篇哈夫曼树