看数据结构写代码(57) AVL树的删除

来源:互联网 发布:mysql dns 热切 编辑:程序博客网 时间:2024/06/05 10:09

上一节 已经说了 AVL树的插入 操作,可是 只有 插入,没有删除,怎么能叫 动态 查找表呢。

呵呵,博主 赶紧 去 研究了一番。下面 是成果:


AVL树的删除 大致 分为 两大块: 1. 查找节点 并 删除 2. 保持 删除 后 平衡因子的 影响


1. 首先 找到 这个 节点,如果 节点 不存在,直接 退出 函数

if (*tree == NULL){//没找到return false;}


2.如果 存在,分为 四种情况:(根 二叉 排序树的 删除 类似)

1.节点 为 叶子 节点,直接 删除

2.节点 的 左子树为空,则 用 节点的 右子树 代替 节点,并删除 这个节点。

3.节点的 右子树为空,则用 节点的 左子树 代替节点,并 删除 这个 节点

4.左右子树 都不为空 (下面 这样做,是为了 减少 旋转的 次数 ,如果 不懂,请 往下看,看完,再回头看)

   4.1 如果 节点的 左子树的高度 >= 右子树的高度(LH,EH),则 从 左子树里 寻找 值 最大节点将最大值赋值 给 节点,并删除 最大节点

   4.2如果节点 的 左子树的高度 《 右子树的高度(RH),则从 右子树里 寻找 值 最小的节点,并将 最小值 赋值给 节点,并删除最小节点


                 if (data == key){if (p->lChild == NULL){//叶子节点 或者 左孩子为空*tree = p->rChild;//free(p);*shorter = true;}else if(p->rChild == NULL){//右子树为空*tree = p->lChild;free(p);*shorter = true;}else{//左右子树 都不为空if (p->bf == LH || p->bf == EH){//左高,或者等高,//从左子树里寻找 最大节点(左子树的 最右下角)AvlTree lc = p->lChild;while (lc->rChild != NULL){lc = lc->rChild;}p->data = lc->data;//替换以后 ,然后删除 替换节点//然后从 左子树里 寻找删除节点,并删除它.deleteAvlTree(&p->lChild,p->data,shorter);}else{//右高,从 右子树里寻找 最小的 替换 AvlTree rc = p->rChild;while (rc->lChild != NULL){rc = rc->lChild;}p->data =  rc->data;//然后从 右子树里寻找删除节点,并删除它deleteAvlTree(&p->rChild,p->data,shorter);}}

3.至此, 删除 操作 已 完成了,可是删除后,必定 会 造成 树的 不平衡,怎么 去除 这些影响呢。我们通过 上节 说的 旋转 来消除影响。

分为 两种情况:删除的 是 节点的 左子树  和 删除节点的 右子树。

删除的 是 节点的 左子树:分为三种情况。

1.如果 节点 的 平衡因子 为 1 (LH),删除后 ,平衡因子 变 为0, 变 矮了。

2.如果 节点的 平衡因子 为0(EH),删除后 节点 的 平衡 因子 变为 -1(RH), 没有 变矮

3.如果 节点的 平衡因子 为-1(RH),删除 左子树之后,节点的 平衡因子 变 为(-2),右边的 部分 失去 平衡了,我们 需要 对节点 进行 右平衡。(删除的 右平衡 和 插入的 右平衡 稍微 有点 不同,在 最后 会说到)。右平衡 会 根据 节点 右子树的 平衡因子 来 判断 是否变矮了

                           if (*shorter == true){//switch (p->bf){     case LH:{//删除前 左子树高,删除左子树后,边矮了p->bf = EH;*shorter = true;//*shorter = false;写错了break;}case EH:{//之前 等高,删除后,右高p->bf = RH;*shorter = false;break;}case RH:{//之前右高,删除左子树之后,右边失去平衡//和前面的步骤不能反..if (p->rChild->bf == EH){//自己画图,想一想*shorter = false;}else{//左孩子之前 不等高,必会变矮*shorter = true;}rightBalance(tree);break;}}

同样 删除的 是 节点 的 右子树,也有三种情况:

1.节点 的平衡因子为0,(EH),节点的平衡因子 变为 1(LH), 没有变矮。

2.节点的平衡因子为-1(RH),节点的 平衡因子 边为0(EH),变矮了。

3.节点的 平衡因子为1(LH),删除右子树后,左边 失衡了,需要 对其 进行 左平衡化。(同样 删除的 左平衡 和 删除的  做平衡 略微 有些区别)。根据 节点的 左子树的平衡因子 来 判断 是否 变矮了。(可以 画图 来 看)。

                       if (*shorter == true){//删除右子树后,边矮了switch (p->bf){case LH:{//左边 失去平衡//if (p->rChild->bf == EH){//画图考虑 考虑// 这一块 还是不太明白//顺序可以颠倒.if(p->lChild->bf == EH){*shorter = false;}else{*shorter = true;}leftBalance(tree);break;}case EH:{//之前 等高,删除右子树,左面边高了,整体没有变矮p->bf = LH;*shorter = false;break;}case RH:{//之前右高,现在等高,边矮了p->bf = EH;*shorter = true;//*shorter = false;写错了break;}}


至此 删除 代码 已经全部 说完, 删除 函数 完整代码 如下:

/avl树删除//返回 :删除成功 返回 true,没找到 返回 false//shorter : 是否变短了bool deleteAvlTree(AvlTree * tree,TreeType key,bool * shorter){if (*tree == NULL){//没找到return false;}else{AvlTree p = *tree;TreeType data = p->data;if (data == key){if (p->lChild == NULL){//叶子节点 或者 左孩子为空*tree = p->rChild;free(p);*shorter = true;}else if(p->rChild == NULL){//右子树为空*tree = p->lChild;free(p);*shorter = true;}else{//左右子树 都不为空if (p->bf == LH || p->bf == EH){//左高,或者等高,//从左子树里寻找 最大节点(左子树的 最右下角)AvlTree lc = p->lChild;while (lc->rChild != NULL){lc = lc->rChild;}p->data = lc->data;//替换以后 ,然后删除 替换节点//然后从 左子树里 寻找删除节点,并删除它.deleteAvlTree(&p->lChild,p->data,shorter);}else{//右高,从 右子树里寻找 最小的 替换 AvlTree rc = p->rChild;while (rc->lChild != NULL){rc = rc->lChild;}p->data =  rc->data;//然后从 右子树里寻找删除节点,并删除它deleteAvlTree(&p->rChild,p->data,shorter);}}return true;}else if(data > key){if (deleteAvlTree(&p->lChild,key,shorter) == false){//没找到return false;}if (*shorter == true){//switch (p->bf){case LH:{//删除前 左子树高,删除左子树后,边矮了p->bf = EH;*shorter = true;//*shorter = false;写错了break;}case EH:{//之前 等高,删除后,右高p->bf = RH;*shorter = false;break;}case RH:{//之前右高,删除左子树之后,右边失去平衡//和前面的步骤不能反..if (p->rChild->bf == EH){//自己画图,想一想*shorter = false;}else{//左孩子之前 不等高,必会变矮*shorter = true;}rightBalance(tree);break;}}}return true;//删除成功}else{if (deleteAvlTree(&p->rChild,key,shorter) == false){//没找到return false;}if (*shorter == true){//删除右子树后,边矮了switch (p->bf){case LH:{//左边 失去平衡//if (p->rChild->bf == EH){//画图考虑 考虑// 这一块 还是不太明白//顺序可以颠倒.if(p->lChild->bf == EH){*shorter = false;}else{*shorter = true;}leftBalance(tree);break;}case EH:{//之前 等高,删除右子树,左面边高了,整体没有变矮p->bf = LH;*shorter = false;break;}case RH:{//之前右高,现在等高,边矮了p->bf = EH;*shorter = true;//*shorter = false;写错了break;}}}return true;}}}

最后 得 说一说  ,插入的 左(右)平衡 代码 和 删除的 左(右)平衡代码的 区别。

其实 就多了 一种 节点 左子树 平衡因子的 情况,插入 没有 Case EH 的情况,删除 有 Case EH的情况。

//左平衡void leftBalance(AvlTree * tree){AvlTree p = *tree;AvlTree lc = p->lChild;switch (lc->bf){//case LH:{//LL型,插入在左子树的左子树上p->bf = lc->bf = EH;R_Rotate(tree);//R_Rotate(tree);尽量按上面来写//p->bf = lc->bf = EH;break;}case RH:{//LR型,插入在左子树的右子树上AvlTree rc = lc->rChild;switch (rc->bf){//设置 tree ,lc的平衡因子case LH:{//插入在 rc 的 左子树上p->bf = RH;lc->bf = EH;break;}case EH:{//这个真没想明白。。。可能是 EH吗p->bf = lc->bf = EH;break;}case RH:{//插入在rc的右子树上p->bf = EH;lc->bf = LH;break;}}rc->bf = EH;//左子树的右子树 的平衡因子 必为 “等高”L_Rotate(&(*tree)->lChild);//先左旋转 左子树R_Rotate(tree);//在右旋转 根节点break;}case EH:{//insertAVL用不着,deleteAVL得用p->bf = LH;lc->bf = RH;R_Rotate(tree);break;}}}

//右平衡void rightBalance(AvlTree * tree){AvlTree p = *tree;AvlTree rc = p->rChild;switch (rc->bf){case LH:{//插入在右子树的 左子树上AvlTree lc = rc->lChild;switch (lc->bf){//设置 p,lc的平衡度case LH:{//插在 lc的左子树上p->bf = EH;rc->bf = RH;break;}case EH:{p->bf = rc->bf = EH;break;}case RH:{//插在lc的右子树上.p->bf = LH;rc->bf = EH;break;}}lc->bf = EH;//右子树的左子树 最终平衡R_Rotate(&(*tree)->rChild);//先平衡右子树L_Rotate(tree);//再平衡根节点break;//就因为少了一个break...调试了半天。。}case RH:{//插入在 右子树的 右子树上,左旋转p->bf = rc->bf = EH;L_Rotate(tree);break;//L_Rotate(tree);//p->bf = rc->bf = EH;尽量按上面的来写}case EH:{//同样 insertVAL用不着,deleteVAL得用p->bf = RH;rc->bf = LH;L_Rotate(tree);break;}}}

至于 case  EH 的 具体 细节 怎么来的,请 画图。


觉得  AVL 树的 插入 和 删除 最难的 地方 就是 如果 计算 平衡因子。 这些 需要 通过 画图 来得知。

参考 网址:http://blog.csdn.net/sysu_arui/article/details/7897017


完整 代码 包括 测试 用例 的 工程 文件 网盘地址:http://pan.baidu.com/s/1hqGhXpq



1 0