持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情
在二叉排序树中删除结点
对于一般的二叉树来说,删去树中一个结点没有意义,因为它将使得以被删结点为根的子树成为森林,破坏了整棵树的结构。因此,在二叉排序树中删除一个结点是指仅删除指定结点,而不是把以该结点为根结点的子树删掉。显然, 删除指定结点以后的二叉树仍然要保持二叉排序树的性质。
若要删除的结点由变量p指出,其双亲结点由变量q指出,则删除 p所指的结点应该从下面4种情况分别考虑:
- 若被删除结点为叶结点,则删除比较简单,可以直接进行删除(还要将其双亲结点相应的指针域置NULL)。
- 若被删除结点没有左子树(右子树存在),则可以用其右子树的根结点取代被删除结点的位置。
- 若被删除结点没有右子树(左子树存在),则可以用其左子树的根结点取代被删除结点的位置。
- 若被删除结点的左、右子树均存在,则要找到被删除结点右子树中值最小的结点(不妨假设由r指出该结点的位置),并用该结点取代被删除结点的位置。因为由r指出的结点一定没有左子树(为什么?),所以用其右孩子来取代r所指结点的位置。
//删除元素
void DELELEBST(BTREE *T, BTREE p, BTREE q) {
BTREE r, s; // 其中 r 是删除p后待和q项链的节点,即r是取代p的节点
int flag = 0; // flag的作用的用来标志待删除节点p是不是根节点,书上这段代码可以更优化
if (p->lchild == NULL) { //待删除节点p的左节点为空,则将r为p的右子树
if (p == (*T))
*T = p->rchild;
else {
r = p->rchild;
flag = 1;
}
} else if (p->rchild == NULL) { //待删除节点p的右节点为空,则将r为p的左子树
if (p == (*T))
*T = p->lchild;
else {
r = p->lchild;
flag = 1;
}
} else { // 如果左右两个节点都不为空
r = p->rchild;
s = p;
while (r->lchild != NULL) { //找到右子树最左侧的节点。s为r的双亲节点
s = r;
r = r->lchild;
}
r->lchild = p->lchild; // 将r的左侧指向p的左侧
//如果s==p,则最左侧节点就是p的右子节点r。这时就不用再修改r的右子节点了,
if (s != p) { // 如果s != p ,则说明p的右子节点有左子树,此时需要将最左侧节点的右子节点和其双亲节点相邻,及将r节点从p的右子树中移除
s->lchild = r->rchild;
r->rchild = p->rchild; // 此时需要修改r的右指针的指向,
}
if (p == (*T))
*T = r;
else
flag = 1;
}
if (flag == 1) {
if (p == q->lchild)
q->lchild = r;
else
q->rchild = r;
}
free(p);
}
优化后的代码
//代码优化,和上面一样,只是将判断p是不是根节点给移到最后,简化了代码
void DELELEBST1(BTREE *T, BTREE p, BTREE q){
BTREE r, s; // 其中 r 是删除p后待和q项链的节点,即r是取代p的节点
if( p->lchild == NULL )
r = p->rchild;
else if ( p->rchild == NULL)
r = p->lchild;
else{
s= p ;
r = p->rchild;
while (r->lchild != NULL){
s = r ;
r = r->lchild;
}
r->lchild=p->lchild;
if( s != p){
s->lchild=r->rchild;
r->rchild=p->rchild;
}
}
if(p==(*T))
*T=r;
else{
if(p==q->lchild)
q->lchild=r;
else
q->rchild=r;
}
free(p);
}
在删除结点的算法中,当p所指结点的左、右子树都存在时,也可以用p结点的左子树中值最大的结点来取代p结点,因此,删除结点的算法不是唯一的,并且需要对建立二叉排序树的原则进行相应的修改。