从0开始学习数据结构-树与二叉树-二叉排序树②

189 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第24天,点击查看活动详情

在二叉排序树中删除结点

对于一般的二叉树来说,删去树中一个结点没有意义,因为它将使得以被删结点为根的子树成为森林,破坏了整棵树的结构。因此,在二叉排序树中删除一个结点是指仅删除指定结点,而不是把以该结点为根结点的子树删掉。显然, 删除指定结点以后的二叉树仍然要保持二叉排序树的性质。

若要删除的结点由变量p指出,其双亲结点由变量q指出,则删除 p所指的结点应该从下面4种情况分别考虑:

  1. 若被删除结点为叶结点,则删除比较简单,可以直接进行删除(还要将其双亲结点相应的指针域置NULL)。
  2. 若被删除结点没有左子树(右子树存在),则可以用其右子树的根结点取代被删除结点的位置。
  3. 若被删除结点没有右子树(左子树存在),则可以用其左子树的根结点取代被删除结点的位置。
  4. 若被删除结点的左、右子树均存在,则要找到被删除结点右子树中值最小的结点(不妨假设由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结点,因此,删除结点的算法不是唯一的,并且需要对建立二叉排序树的原则进行相应的修改。