AVL树的删除算法的两种实现方法
本文是本人原创,当时作为长安大学高一凡老师带的学生,我的毕业设计就是做一个AVL 算法的演示软件。其中,AVL树的删除算法在度娘和 谷歌了很久都没有找到逻辑通畅或者说是我能够看懂的。后来,闭门造成,费了十几张A4纸才把算法设计出来
本文是当时我想投稿的一个草稿,只是当时太嫩,不敢投论文。。而且马上就毕业了,所以就一直没了下文。现,贡献该草稿,希望对要了解AVL树算法的同学有帮助。
<原创证明>邮件来往。
摘要:AVL树是一种重要的数据结构,凡是能够用二叉查找树的地方都能用AVL树代替,而且其效率最坏也是 .一般的数据结构书上通常都只介绍AVL树的插入算法。本文将讨论AVL树删除算法的递归和非递归实现及其可视化实现。
关键字:AVL树;删除;递归;非递归;可视化
1.删除结点的分类
对于要删除的结点设为E可以分为三种类型,如图1 所示
(1) 叶子结点
(2) 没有左子树且有右子树的结点
(3) 有左子树的结点
对于叶子结点E可以直接删除,而对于只有右子树ER的结点可以将ER的值赋给E然后将ER释放并将E右孩子指空。
对于第(3)种则要在EL中找到E的中序遍历的前驱结点。E的前驱结点为EL中值最大的结点。将前驱结点的值赋给E且删除前驱结点。
2 删除结点后的失衡类性及其调整
删除结点后可能导致其祖先结点的不平衡。由于左右对称,我们仅分析删除发生在右子树的情况。我们将各种情况删除前,删除后以及调整后整个过程的变化用图示的方法表示。
图2.1描述的是在T的右子树TR中删除结点后导致T失衡,须进行R_Rotate(T)处理,处理之后各结点的平衡因子如图所示,且树不变矮。
图2.2描述的是在T的右子树TR中删除结点后导致T失衡,须进行R_Rotate(T)处理,处理之后各结点的平衡因子如图所示,且树的高度减1 。
图2.3描述的是在T的右子树TR中删除结点后导致T失衡,须进行LR_Rotate(T)处理,处理之后各结点的平衡因子如图所示,且树的高度减1 。
图2.4描述的是在T的右子树TR中删除结点后导致T失衡,须进行LR_Rotate(T)处理,处理之后各结点的平衡因子如图所示,且树的高度减1 。
图2.5描述的是在T的右子树TR中删除结点后导致T失衡,须进行LR_Rotate(T)处理,处理之后各结点的平衡因子如图所示,且树的高度减1 。
另外还有两种不发生失衡的情况如图2.6和图2.7所示
图2.6描述的是在T的右子树TR中删除结点后的T的平衡因子变为1,树的高度不变
图2.7描述的是在T的右子树TR中删除结点后的T的平衡因子变为0,树的高度减1.
3 实现算法
3.1 数据结构
template<typename T>struct BSTNode//平衡二叉树的结点类型结构体
{
T data;//结点数据类型
int bf;//结点的平衡因子,比二叉链表结点多此项
BSTNode<T>*lchild,*rchild;//左右孩子指针
};
3.2 算法
3.2.1 递归法
递归实现AVL树的结点删除的思想如下。
在AVL树T上删除值为E的结点步骤如下:
(1)若树T为空,返回0退出否则到(2);
(2)比较T的数据和E,若相等跳到(3),若E小于T->data跳到(4),若E大于T->data则跳到(5)
(3)分析T结点的类型
(3.1)若T是叶子结点则直接删除,树变矮即lower=1;
(3.2)若T只有右子树TR则将右子树结点的值赋给T,删除TR结点将T->rchild=NULL,lower=1;
(3.3)若T有左子树,则找到其中序遍历的前驱结点P,将P结点值用临时变量temp保存,并且用指针Tptr保存T;递归删除结点P;将Tptr所指结点即原T结点的值更新为temp;
(4)在左子树T->lchild中递归删除值为E的结点,若删除成功检查左子树是否变矮即lower的值是否为1;若lower=0返回0退出;若lower=1则进行失衡调整各种情况上文有分析
(5)在右子树T->rchild中递归删除值为E的结点,若删除成功检查左子树是否变矮即lower的值是否为1;若lower=0返回0退出;若lower=1则进行失衡调整,各种情况上文有分析
3.2.2非递归法
非递归的算法思想如下。
在AVL树T上删除值为E的结点的步骤如下:
初始化一个栈s ;
(1) 若树T为空则返回0退出否则跳到(2);
(2) p为工作指针p=T;比较p的数据和E,若相等则跳到(3),若E小于p->data则跳到(4),若E大于p->data则跳到(5)
(3) 分析p结点的类型
(3.1)若p是叶子结点则直接删除,树变矮,
(3.1.1)若p是其父结点的左子树则lower=1;
(3.1.2)若p是其父结点的右子树则lower=2;
(3.2) 若p只有右子树pR则将右子树结点的值赋给p然后删除删除pR,树变矮,lower的值 和(3.1)一样分析,之后不再特别说明则都和(3.1)处理一样。
(3.3) 若p有左子树pL则将p和pL入栈,且用指针tempptr指向p即tempptr=p然后执行
q=TL ; While(q->rchild){q=q->rchild;s.push(q);}之后可以找到p的前驱结点q,将q的值赋给原p结点即tempptr->data=q->data,若q是叶子结点则直接删除否则将q的左子树代替q。树变矮且弹栈s.pop() ;然后执行如下循环体
(3.3.1)若栈不空且lower不为0则执行
a.弹栈 q=s.top();s.pop();及其q的父结点fa=s.top();
若fa=NULL则表明q是根结点则若要执行下面 b
或c时不用在给lower赋值了。
b.若lower=1 则表明左子树变矮,执行相应的失衡调整,并且调整过程中若导致q树变矮也得根据q为fa的左子树还是右子树将 lower=1或为lower=2;
c. 若lower=2 则表明右子树变矮,执行相应的失衡调整,并且调整过程中若导致q树变矮也得根据q为fa的左子树还是右子树将 lower=1或为lower=2;
退出循环后返回0退出;
(4)将p入栈且p=p->lchild;然后跳到(2);
(5)将p入栈且p=p->rchild;然后跳到(2);
4实例分析
有如图3.1所示的AVL树
图3.1
选择删除61结点后如
5结束语
无论是递归还是非递归实现AVL树的删除都比较复杂,将其总结为算法并配以图形化的实现使得AVL树的在课堂上讲解更为方便,继而有力的推广AVL树的应用。
6参考文献
严蔚敏,吴伟民.数据结构(C语言版)[M].北京:清华大学出版社,2007.