二叉搜索树又称为二叉排序树,它具有二叉树以下的性质:
-若它的左子树不为空,则左子树上所有的结点的值都小于根节点的值;
-若它的右子树不为空,则右子树上所有的结点的值都大于根节点的值;
-它的左右子树也分别为二叉搜索树。
二叉树搜索树的中序一定有序。
int[] a={5,3,7,1,4,6,8,9};
中序遍历:{1,3,4,5,6,7,8,9};
搜索二叉树的遍历和插入比较简单,所以在这主要讲解一下二叉搜索树的删除操作,我看老师讲解的二叉树的删除操作视频看了好几遍,还看了好几篇讲解二叉搜索树删除操作的,终于理解了,所以想用自己的方法记录一下这个学习的过程。
删除操作可以分为3种情况:node为待删除的节点,parent为待删除结点的父节点
1.node左右孩子均不存在
2.node只有左孩子或者只有有孩子
3.node左右孩子均存在
第一种情况:当待删除结点node的左右孩子均不存在的时候,那么它是叶子结点或者只有一个根结点的树;删除的时候如果node是根节点,则让根结点为空;如果node为其父节点的左孩子,则把父亲左孩子置为空;否则把父亲右孩子置为空。(如果这一段文字看不懂,可以自己画个图试试看,相信你自己)
代码如下:
if(node.left==null&&node.left==null){
if(node==root){
root=null;
}else if(node==parent.left){
parent.left=null;
}else{
parent.right=null;
}
}
第二种情况:当待删除的结点node只有左孩子或者右孩子时,那么在删除的时候要断两个联系,node和parent,node和它的孩子结点的联系;再新建一个一个联系;如下图所示:当要删除的结点没有左孩子时,就是下图中左边的这种情况:
1.首先如果待删除结点为根节点,则让待删除的节点的左孩子为根;
2.如果待删除结点为其父节点的左孩子,则让待删除的结点的左孩子指向其父节点。
3.如果待删除结点为其父节点的右孩子,则让待删除的结点的左孩子指向其父节点。
if(node.left!=null&&node.right==null){
//有左孩子,没有右孩子
if(node==root){
root=node.left;
}else if(node==parent.left){
parent.left=node.left;
}else{
parent.right=node.left;
}
}else if(node.left==null&&node.right!=null){
//有右孩子,没有左孩子
if(node==root){
root=node.right;
}else if(node==parent.left){
parent.left=node.right;
}else{
parent.right = node.right;
}
}
第三种情况:
当待删除的结点左右孩子都有时,此时采用替换法,定义两个节点,ghost和ghostParent,ghost中存放待删除结点左子树中最大的结点(或者右子树中最小的值替换),ghostParent保存ghost的父节点,找到ghost后,用ghost的值替换待删除结点node的值,然后判断待删除结点是否有左孩子,如果有,则让其左孩子指向其父节点(ghostParent)。
Node ghost=node.left;
Node ghostParent=null;
while(ghost.right!=null){
ghostParent=ghost;
ghost=ghost.right;
}
//进行替换
node.key=ghost.key;
//删除ghost结点(其右孩子一定为空)
if(node==ghostParent){
ghostParent.left=ghost.left;
}else{
ghostParent.right=ghost.left;
}
public boolean remove(Integer key){
//找要删除的结点key所在的结点为node,node的双亲结点,记作parent
Node current=root;
Node parent=null;
while(current!=null){
int comp=key.compareTo(current.key);
if(comp==0){
removeInternal(current,parent);
return true;
}else if(comp<0){
parent=current;
current=current.left;
}else{
parent=current;
current=current.right;
}
}
return false;
}
private void removeInternal(Node node,Node parent){
//总共有四种情况:1.两边都没有左右孩子 2.只有一边有孩子 3.两边都有子孩子
//node为要删除的结点
if(node.left==null&&node.left==null){
if(node==root){
root=null;
}else if(node==parent.left){
parent.left=null;
}else{
parent.right=null;
}
}else if(node.left!=null&&node.right==null){
//有左节点,没有右节点
if(node==root){
root=node.left;
}else if(node==parent.left){
parent.left=node.left;
}else{
parent.right=node.left;
}
}else if(node.left==null&&node.right!=null){
if(node==root){
root=node.right;
}else if(node==parent.left){
parent.left=node.right;
}else{
parent.right = node.right;
}
}else{
//左右孩子都有的时候
Node ghost=node.left;
Node ghostParent=null;
while(ghost.right!=null){
ghostParent=ghost;
ghost=ghost.right;
}
//进行替换
node.key=ghost.key;
//删除ghost结点(其有孩子一定为空)
if(node==ghostParent){
ghostParent.left=ghost.left;
}else{
ghostParent.right=ghost.left;
}
}
}
1.对于删除操作,其实代码不复杂,前两种情况考虑起来也比较简单,主要是第三种情况,有点复杂,采用替换法的思想,找待删除结点左子树中最大的值替换或者右子树中最小的值替换,才能保证二叉搜索树的结构完整。
2.我们老师说过,写代码其实是做题的最后一步,首先你要学会分析一个题,最好把思路能在自己的脑子中过一遍,结合画图,想明白过程之后再动手,这才是提高自己代码能力的有效途径。