什么是二叉搜索树
二叉搜索树对于任何节点x,其左子树中的关键字最大不超过x的关键字。其右子树中的关键字最小不低于x的关键字。如图所示:
查询二叉搜索树
一、在二叉搜索树中查找指定的值,可以利用二叉树的性质很容易就很写出代码:
1、如果值等于根节点的值,结束;
2、如果值大于根节点,递归的在右子树中查找;
3、如果值小于根节点,递归的在左子书中查找;
TRee_search(x,k){
if x==NULL && k == x.key
return x;
if (k < x.key)
return Tree_search(x.left,k);
else
return Tree_search(x.right,k);
}
由于递归效率较低,我们可以展开递归的过程
TRee_search(x,k){
while(x != NULL && k != x.key){
if(key < x.key)
x = x.left;
else
x =x.right;
}
return x;
}
二、查询最大关键元素和最小的关键元素
由二叉搜索树的性质可以很容易的得出:
1、最小的元素一定在左子树中;
2、最大的元素一定在右子树中;
Tree_MAX(x){
while(x.right !=NULL)
x = x.right;
return x;
}
Tree_MIN(x){
while(x.left !=NULL)
x = x.left;
return x;
}
三、查找后继
查找一个元素的后继节点分为两总情况:
1、如果节点x的右子树非空,那么x的后继就是x的右子树中最小的值;
2、如果节点x的右子树为空,但是有一个后继节点y,那么y就是x的最底层的祖先,并且y的左孩子也是x的祖先,找到y只需要简单地重x开始沿树而上直到遇到节点:这个节点是父节点的左孩子。
Tree_Successor(x){
if x.right != NULL
return Tree_MIN(x.right)
y = x.p // 父节点
while(y!=NULL && x==y.right){
x = y;
y = y.p;
}
return y;
}
四、插入一个元素
插入的元素不能破坏搜索树的性质。
Tree_insert(T,z){
y = new TreeNode();
x = T.root;
while(x !=NULL){
y = x;
if (z.key < x.key)
x = x.left;
else
x = x.right;
}
z.p = y;
if(y == NULL)
T.root = z;
else if(z.key < y.key)
y.left = z;
else
y.right = z;
}
五、 删除元素
删除元素的问题比较困难,因为删除之后不能破坏搜索二叉树的结构,删除元素的情况可以分为以下三种:
1、如果要删除的节点z没有孩子节点,这种情况最简单,直接把节点删除就可以;
2、如果要删除的节点z只有一个孩子,那么把这个孩子提升到z在树中的位置就可以;
3、如果要删除的节点z有两个孩子,找到z的后继元素y(y一定没有左子树有兴趣的同学可以证明以下反正法很好证明)这种情况又分为两种情况:
3.1 如果后继元素y是z的孩子,那么用y替换z的位置,y的左子树=z的子树
3.2 如果后继元素y不是z的孩子,那么用y的右孩子x来代替y的位置,然后y代替z的位置。
为了在二叉搜索树中移动子树,定义移动子树的过程Transolant,作用是用一棵子树代替另一颗子树的位置,假设用根节点为v的子树替换以u为根节点的子树,那么节点u的双亲变为v的双亲
Transolant(T,u,v){
if(u.p==NULL)//u没有父节点
T.root = v;
else if(u == u.p.left)//u是父节点的左孩子
u.p.left = v;
else
u.p.right = v;
if (v != NULL)
v.p = u.p//改变父节点的指向
}
Tree_Delete(T,z){
if(z.left==NULL)//处理只有一个孩子的问题
Transolant(T,z,z.right);
else if(z.right==NULL)
Transolant(T,z,z.left);
else
y = Tree_MIN(z.right)//找到z的后继节点:这里要记住如果一个节点有两个孩子节点,那么他的后继节点没有左孩子,前驱节点没有右孩子
if(y.p !=z){//处理情况3.2
Transolant(T,y,y.right);
y.right = z.right;
y.right.p = y;
}
Transolant(T,z,y);
y.left = z.left;
y.left.p = y;
}