avl树相比于搜索二叉树每个结点是多了个平衡因子bf,avl树时时刻刻要维持树中的每个结点的平衡因子的绝对值小于等于1.
avl树的插入操作:
avl树因为要保证每个结点的平衡因子要时时刻刻都符合要求,则树中每插入一个结点,都可能引起平衡被打破,所以每次插入一个结点,都要从插入的结点往上进行检查是否有哪个结点需要调整. 要在插入新结点后进行平衡检查,则需要把插入结点的插入过程的下行路线上的每一个结点都依次记录下来,这个可以借助于栈来实现,在查找插入位置的过程,把每一个结点指针放入栈中.
现在来说一下具体的过程:
从根结点开始,首先查找要插入的位置:
如果结点值相等则返回错误,如果小于则向左走,如果大于则向右走,把这个过程中的每一个结点都放入一个栈中,这样直到到达叶子结点,即找到了插入的位置.然后new出来一个结点进行插入(如果这个位置的父亲结点(stack中的top)是NULL,则是空树,让root指针指向该结点,插入完成,如果父亲结点不是NULL,则根据父亲结点和本结点的值的大小确定是左边插入还是右边插入).
插入完成以后进行平衡调整:
取出栈中的元素进行检查:插入的结点对于取出的结点如果是左边插入,则平衡因子减1,如果右边插入则平衡因子加1.
如果加减1以后平衡因子是0,即意味着插入节点之前平衡因子只能是正负1,插入该节点以后,该子树的左右子树高度相等,因此并不改变该子树的高度,也就并不影响整棵树的高度,所以树是平衡的,不需要调整,调整结束,break ;
如果平衡因子是+1或者-1,则意味着该节点所在的子树的高度发生变化,(因为在此之前该节点的平衡因子只能是0),所以以该节点为root 的子树的高度一定是增加了,所以要向上继续检查是否有哪个节点的平衡因子因为插入了一个节点平衡因子变为正负2,所以继续取出stack中的下一个节点进行上述同样的检查;
如果平衡因子是正负2,则平衡打破,需要进行调整,下面详述调整过程:
根据平衡子是+2还是-2分为两种情况:
1.平衡因子是-2:
如果该节点的孩子节点平衡因子是负值:则对该节点进行一次左旋转即调整完成;
如该该节点的孩子节点的平衡因子是正值:则需要进行先左后右旋转.
2.平衡因子是+2:
如果该孩子节点bf 是正值:则对该节点进行一次右旋转即可;
如果孩子节点bf是负值:则对该节点进行先右后左旋转即可.
调整平衡完成以后需要将该子树的新根节点挂到之前的根节点下面.以上即整个插入过程.
AVL树的删除操作:
首先查找要删除的节点,找到以后,要删除的节点分为两种情况:
1.要删除的节点左右两个孩子都存在,直接删除不方便,则在右子树中查找最小的节点,将其值替换为要删除的节点的值,因为右子树的最小节点必然没有左孩子,即只有一个孩子.然后问题转化为删除这个右子树中最小的节点.(或者也可以将问题转化为删除左子树里最大的节点)
2.要删除的孩子节点只有一个孩子节点.
以上均为要删除的节点只有一个孩子节点,则直接将仅有的一个孩子节点提上来即可.
删除以后将进行从删除节点向上进行平衡性的检查:(在查找要删除的节点的过程中,将经过的路径上的节点全部存放到一个stack)
->取出栈顶元素pr并弹出栈顶元素,如果删除的节点x比该节点pr的值小,则必定是左树删除,则pr-bf++否则pr->bf–.
1.如果pr->bf的绝对值是1:则在删除节点之前,pr的bf是0,即左右平衡,删除了以后左树或者右树少了一个节点,但pr这个子树的高度并没发生变化.对与pr的上面的所有节点而言树高并没有发生变化,所以调整完成.
2.如果平衡因子变为0:则在删除之前平衡因子是+1或者-1,现在删除节点以后变为0,则pr子树的高度减1,则要向上继续检查,情况重新回到->上面去检查..
3.如果平衡因子的绝对值是2:则平衡打破,进行平衡调整:
下面详细描述调整过程:(pr的孩子节点是p)
(1)q->bf == 0,如果pr->bf>0 :则左旋转,否则右旋转,旋转完成以后调整平衡因子大小.
(2)q->bf > 0, 如果 pr->bf>0:则左旋转,否则先左后右旋转.
(3)q->bf<0, 如果pr->bf>0:则先右后左旋转,否则右旋转.
AVLTree的具体代码详解后面的代码片.
还有关于左旋转,右旋转,先左后右旋转,先右后左旋转,还有旋转过程的平衡因子如何改变具体详见后面文章.