目录
什么是二叉搜索树
二叉搜索树是由二叉树组织的,如下:和普通二叉树相比多了parent结点,这样可以匹配搜索二叉树性质方便实现相应的功能;二叉搜索树有那些性质呢:
对任何结点x,其左子树中的关键字最大不超过x.key,其右子树中的关键字最小不低于x.key.
class Node {
public:
int key;
node*left;
node*right;
node*parent;
};
我们可以使用中序遍历方法遍历这颗树,这样结果就是有序的
上图所示搜索二叉树中序遍历结果为:1,2,3,5,6,7,13
void InOrderTreeWalk(Node* root) {
if (root == nullptr)return;
InOrderTreeWalk(root->left);
cout << root->key << " ";
InOrderTreeWalk(root->right);
}
二叉搜索树的查询
二叉搜索树的查询有多种,如查找关键字,最小值,最大值,前驱(中序遍历中该结点前一个结点),后继(中序遍历中该结点的下一结点);
关键字查找
根据二叉搜索树的性质,我们很容易想到,结点左边数小于它,右边反之,所以找到就返回,小于去左树,大于去右树
递归代码:
Node *TreeSearch(Node*root, int k) {
if (root == nullptr || root->key == k) {
return root;
}
if (k < root->key) {
return TreeSearch(root->left, k);
}
return TreeSearch(root->right, k);
}
非递归代码:
Node *IterativeTreeSearch(Node*root, int k) {
Node*x = root;
while(x != nullptr && x->key != k) {
if (x->key < k) {
x = x->left;
}
else {
x = x->right;
}
}
return x;
}
查找最小值
因为结点左边值小于它,右边大,所以只需返回最左结点,我们假设根节点不为空
Node *TreeMinimum(Node*root) {
Node*x = root;
while (x->left != nullptr) {
x = x->left;
}
return x;
}
查找最大值
最大值一定是最右结点
Node *TreeMaximum(Node*root) {
Node*x = root;
while (x->right != nullptr) {
x = x->right;
}
return x;
}
查找前驱
如果一颗二叉搜索树中所有关键字都不相同,那么结点x的前驱是小于x.key的最大关键字结点,二叉搜索树允许我们不通过比较就可以确定结点前驱,如果前驱存在,下面过程会返回前驱,否则返回NULL
Node*PreSuccessor(Node*root) {
if (root->left != nullptr) {
return TreeMaximum(root->left);
}
Node*x = root, *y = root->parent;
while (y != nullptr&&x == y->left) {
x = y;
y = y->parent;
}
return y;
}
当x有左树时,前驱为左树最大结点,直接调用maximum即可,如上图结点13
当x无左树并且有前驱y时,y就是x最底层祖先,y的右孩子也是它的祖先,只需从x沿树而上直到遇到它是双亲的右孩子,如上图结点9
查找后继
后继与前驱查找方式一样,就不具体介绍了
当x有右树时,后继为右树最小结点
当x无右树并且有后继y时,y就是x最底层祖先
Node*Successor(Node*root) {
if (root->right != nullptr) {
return TreeMinimum(root->right);
}
Node*x = root, *y = root->parent;
while (y != nullptr&&x == y->right) {
x = y;
y = y->parent;
}
return y;
}
插入
插入比较简单,搜索到该插入的位置,判断是左树还是右树
void TreeInsert(Node*root, Node*v) {
Node*y = nullptr, *x = root;
while (x != nullptr) {
y = x;
if (v->key < x->key) {
x = x->left;
}
else {
x = x->right;
}
}
v->parent = y;
if (y == nullptr) {
root = v;
}
else if (v->key < y->key) {
y->left = v;
}
else {
y->right = v;
}
}
删除
删除操作比较复杂,我们将分为以下三种情况:
如果z没有孩子结点,只删除z,修改父节点
如果z只有一个孩子,就用该孩子替换z位置,修改父节点
如果z有两个孩子,就找z的前驱或后继y,(下面我找的后继),让y替换z,这里需要判断y是否是z的右孩子
但在处理时有四种情况:
如果z没有左孩子,就用右孩子替换,包括右孩子为空;如结点9
如果z有做没右,就用左孩子替换;如13
如果z有两个孩子,查找后继y(后继一定在右树且没有左孩子):
1)如果y是右孩子,用y替换z,
2)y不是右孩子,就先用y右孩子替换y,再用y替换z;如7
下面代码定义了一个移动子树过程
void TransPlant(Node*root, Node*z, Node*y) {
if (z->parent == nullptr) {
root = y;
}
else if (z == z->parent->left) {
z->parent->left = y;
}
else {
z->parent->right = y;
}
if (y != nullptr) {
y->parent = z->parent;
}
}
void TreeDelete(Node*root, Node*z) {
if (z->left == nullptr) {
TransPlant(root, z, z->right);
}
else if (z->right == nullptr) {
TransPlant(root, z, z->left);
}
else {
Node*y = TreeMinimum(z->right);
if (y->parent != z) {
TransPlant(root, y, y->right);
y->right = z->right;
y->right->parent = y;
}
TransPlant(root, z, y);
y->left = z->left;
y->left->parent = y;
}
}
二叉搜索树就写到这里,欢迎大家提问讨论