二叉搜索树
参考:《算法导论》
定义
二叉搜索树首先是一棵二叉树(每个节点至多有两个孩子),每个节点的左子树中节点的键值都不大于它,右子树中的节点键值都不小于它。每个节点的属性包括left(左儿子节点)、right(右儿子节点)、parent(父亲节点),以及该节点的键值data。
如下图所示,图(a)、(b)是一棵二叉搜索树,图c不是一棵二叉搜索树

定义二叉搜索树的数据结构如下所示
using Elem_Type = int;
struct BitSearchTree {
BitSearchTree *left = nullptr, *right = nullptr, *parent = nullptr; //左右儿子和父亲
Elem_Type data; //存储的数据
}*Root; //定义一个根
二叉搜索树的操作主要包括顺序遍历Inorder_Walk、查询节点Find_node、查找最大值节点Maxmum、最小值节点Minimum、查找后继节点Succcessor、查找后缀节点Precursor、插入节点InsertNode、删除节点DeleteNode
操作
顺序遍历
由于二叉搜索树的性质,因此中序遍历(左、根、右)即可按顺序输出二叉搜索树的节点,时间复杂度O(n),n为二叉搜索树的节点数
void Inorder_Walk(BitSearchTree *t) { //从小到大输出二叉树的值(中序遍历)
if (t != nullptr) { //非空
Inorder_Walk(t->left); //遍历左儿子
printf("%d", t->data); //输出当前节点的键值
Inorder_Walk(t -> right); //遍历右儿子
}
}
查询
查询二叉搜索树中是否存在某个键值为x的节点,根据二叉搜索树的性质,先从根开始查询,当查询的键值比当前节点键值大时,向右走,比当前的键值小时向左儿子走,直到找到该值或者到到空节点(未找到)。函数返回键值所在的节点或者,未查到则返回空节点。如下图所示,查询节点1,从根节点开始,5>1,向左走到3,3>1,向左到1,则返回该节点,查询8时,5<8,向右到6,6<8,向右到9,9>8,向左,到空节点。时间复杂度为O(h),h为二叉搜索树的高度

BitSearchTree *Find_node(BitSearchTree *t, Elem_Type x) { //搜索值为x的节点(也可用while循环)
if (t == nullptr) { //空节点,未查到
//cout<<"not find"<<endl;
return t;
}
if (t->data == x) //当前节点的键值与查询的键值相同
return t;
if (x > t->data) //查询的键值比当前节点大,向右走
return Find_node(t->right,x);
return Find_node(t->left, x); //左走
}
//调用:Find_node(root, x),从根开始查
最大值和最小值
根据二叉搜索树的性质,一直向左走即可找到最小键值的节点,一直向右走即可找到最大键值的节点。复杂度为O(h),h为高度
BitSearchTree *Maxmum(BitSearchTree *t,Elem_Type &a) { //查找最大值节点
while (t->right != nullptr) {
a = t->data;
t = t->right; //向右走
}
return t;
}
BitSearchTree *Minimum(BitSearchTree *t, Elem_Type &a) { //查找最小值节点
while (t->left != nullptr) {
a = t->data;
t = t->left; //向左走
}
return t;
}
前驱和后继
节点x的后继是大于x键值的节点中,键值最小的节点,如果x的键值为最大值,则返回空节点。根据二叉树的性质,如果该节点存在右孩子,则其后继为右子树的最小值。如图所示,节点15的后继为右子树的最小值17。如果该节点不存在右子树,则其后继为其祖先中作为左儿子存在的节点的父节点。如图中找15的后继节点,由于15无右孩子,需要向上找,直到6,是作为左儿子存在,故15的后继为6的父亲16。复杂度为O(h),h为高度。

BitSearchTree *Succcessor(BitSearchTree *t, Elem_Type &a) { //查找节点t的后继
if (t->right != nullptr) //存在右儿子
return Minimum(t->right, a); //右子树最小值
BitSearchTree *p = t->parent;
while (p != nullptr&&p->right == t) { //找到作为左儿子存在的节点t
a = p->data;
t = p;
p = p->parent;
}
return p; //返回t的父亲节点p
}
BitSearchTree *Precursor(BitSearchTree *t, Elem_Type &a) { //查找节点t的前驱
if (t->left != nullptr) //存在左儿子
return Maxmum(t->left, a); //返回左子树最大值
BitSearchTree *p = t->parent;
while (p != nullptr&&p->left == t) { //找到作为右儿子存在的节点t
t = p;
a = p->data;
p = p->parent;
}
return p; //返回t的父亲p
}
插入
二叉搜索树的插入都是将新节点插到叶子节点的子节点。如果树非空,从根节点开始,如果插入节点的键值大于当前节点,则向右走;否则向左走。直到叶子节点,将插入的节点作为叶子节点的子节点,左儿子还是右儿子由值决定。复杂度O(h),h为树的高度。
BitSearchTree *InsertNode(BitSearchTree *t, Elem_Type a) { //插入一个值a,t一般为root
BitSearchTree *x =new BitSearchTree(), *z=nullptr;
x->data = a;
while (t != nullptr) {
z = t;
if (a > t->data) //向右走
t = t->right;
else t = t->left; //向左走
}
x->parent = z;
if (z == nullptr) { //树为空
Root = x;
return Root;
}
else if (z->data > a) //和叶子节点相连
z->left = x;
else z->right = x;
return x;
}
删除
删除的情况比较复杂,因为如果删除的节点有孩子,还需要找一个节点代替这个被删除的节点,使得新的二叉树满足二叉搜索树的性质,删除的情况主要分为四种如下图所示,设要删除的节点为z:
- 删除的节点无左儿子
此时直接使其右儿子取代z即可,则易知二叉搜搜索树的性质不变,时间复杂度O(1)

- 删除的节点无右儿子
同理,此时直接使其右儿子取代z即可,二叉搜搜索树的性质不变,时间复杂度O(1)

- z节点左右儿子都存在,其后继恰好是其右儿子,此时用什么节点替代z如何才能保持二叉搜索树的性质不变呢?考虑到后继节点的特点,如果用z的后继替代,则左子树键值依然不大于z,右子树键值不小于z,此时直接用y代替z即可

- z的左右儿子都在,且其后继y不是他的孩子,此时依然要用z的后继节点代替z才能保持二叉搜索树的性质不变。用后继y(y一定没有左孩子)代替z,y的右子树代替y的位置

删除操作时间复杂度为O(h),h为树的高度
首先定义一个Transplant函数,表示使用节点v为根的子树代替节点为u的子树(函数没有更新v原先的左右孩子)
void Transplant(BitSearchTree *u, BitSearchTree *v) { //v为根的子树代替u为根的子树,未更新v的孩子
if (u->parent == nullptr) //代替根节点
Root = v;
else if (u->parent->left == u) //u为左孩子
u->parent->left = v;
else u->parent->right = v; //u为右孩子
if (v != nullptr)
v->parent = u->parent;
}
下面定义删除节点函数
void DeleteNode(BitSearchTree *t,int &a) { //删除节点t,t存储的数据位a
if (t != nullptr)
a = t->data;
if (t->left == nullptr) //t无左儿子
Transplant(t, t->right);
else if (t->right == nullptr) //t无右儿子
Transplant(t, t->left);
else if (t->right->left == nullptr) { //z的后继为其右儿子
Transplant(t, t->right);
t->left->parent = t->right;
t->right->left = t->left;
}
else { //其他情况
int b=0;
BitSearchTree *p = Succcessor(t, b);
Transplant(p, p->right);
Transplant(t, p);
p->left = t->left;
p->left->parent = p;
p->right = t->right;
p->right->parent = p;
}
delete t;
}

浙公网安备 33010602011771号