二叉排序树的删除算法解析
二叉排序树删除操作的几种情况
-
要删除的结点在二叉排序树中是叶子结点:
则可以直接删除,因为删除它们对于整棵树来说,其他节点的结构并不受到影响
-
要删除的结点只有左子树或者右子树:
删除结点后,将它的左子树或右子树移动到删除结点的位置即可(子承父业)
-
要删除的结点既有左子树又有右子树:
简单的想法:让删除结点的左子树成为删除结点的双亲的左子树,然后将删除结点的右子树所有结点进行重新插入
更好的算法:在删除的结点的左右子树中寻找有一个结点来代替删除结点(与其最接近的两个数之一),然后再删除这个用来替换的结点(这个用来替换的结点 一定是一个叶子结点或者只有一个左孩子)
二叉排序树删除算法源码
//若二叉排序树中存在关键字等于key的数据元素,则删除该数据元素并返回TURE;否则返回FALSE
//算法中采用了递归的实现方式
status DeleteBST(BiTree* T, int key){
if(!*T) //不存在关键字等于key的数据元素
return false;
else{
if (key == (*T)->data) //找到关键字等于key的数据元素
return Delete(T);
else if (key<(*T)->data)
//这里T是二叉树的指针,*T得到根节点对象,&表示以引用传递参数(可以对原对象进行修改)(引用修饰整体)
return DeleteBST(&(*T)->lchild,key);
else
return DeleteBST(&(*T)->rchild,key);
}
}
可以看出,其实二叉排序树的删除算法的实现方式和二叉排序树的查找算法几乎完全相同,唯一的不同就是使用了Delete函数,对当前结点进行删除操作。
Delete函数的具体实现
//二叉排序树中删除结点p,并重新连接它的左子树或右子树
status Delete(BiTree* p){
BiTree q, s;
//右子树为空则只需要连接它的左子树
if ((*p)->rchild==NULL){ //*p得到根节点
//free根节点,保留左子树
q = *p; *p = (*p)->lchild; free(q);
}
else if ((*p)->lchild==NULL){ //只需要连接它的右子树
q = *p; *p = (*p)->rchild; free(q);
}
//左右子树均非空
else{
q = *p; s = (*p)->lchild;
while (s->rchild){
//转左,然后向右到尽头(找到待删除结点的直接前驱)(也可以选择找它的直接后继)
q = s; s = s->rchild;
}
//上面while循环的结果为:s为直接前驱,q为s的双亲
(*p)->data = s->data;
//以下就是在删除s结点(这两种情况会在下文进行举例说明)
if (q != *p)
q->rchild = s->lchild;
//如果第一个s就没有右孩子的话,就会有(q = *p),进而出现这种情况
else
q->lchild = s->lchild;
free(s);
}
return true;
}
Delete函数中 q!=*p
的情况
-
如上图所示,将p指向的结点用s指向的结点覆盖
-
之后,按照上文中代码的逻辑,将s结点的左孩子赋值给q结点的右孩子
-
操作的结果如下图所示:
Delete函数中 q=*p
的情况
-
如上图所示,将p指向的结点用s指向的结点覆盖
-
之后,按照上文中代码的逻辑,将s结点的左孩子赋值给q结点的左孩子
-
操作的结果如下图所示: