AVL树
当确定搜索树的高度总是O(logn)时,能够保证每个搜索树操作所占用的时间为O(logn)。
高度为O(logn)的树成为平衡树 (balanced tree)。AVL树就是1962年提出的一种非常流行的平衡树。
定义
AVL树
空二叉树是AVL树;如果T是一棵非空的二叉树,TL和TR分别是其左子树和右子树,那么当T满足以下条件时,T是一棵AVL树:
1)TL和TR是AVL树;
2) | hl - hr | <=1,hl和hr分别是左右子树的高度。
AVL搜索树
AVL搜索树既是二叉搜索树,也是AVL树。
带索引的AVL搜索树
带索引的AVL搜索树既是带索引的二叉搜索树,也是AVL树。
AVL树的高度(O(logn))
假设N_h是一棵高度为h的AVL树中最小的节点数。最坏情况下,左右子树高度分别为h-1,h-2,而且两棵子树都是AVL树。
因此:N_h = N_h-1 + N_h-2 + 1 , N_0 = 0,N_1 = 1
斐波那契数列定义为:F_n = F_n-1 + F_n-2, F_0 = 0,F_1 = 1
有:N_h = F_h+2 - 1, h>=0
由斐波那契定理知道,F_h 约等于Φ^h / √5 ,其中Φ=(1+ √5 )/2,因此N_h = Φ^(h+2) / √5 - 1
如果树有n个节点,那么树的最大高度为:(log以 Φ 为底(√5(n+1)))-2 约等于log以1.62为底2.24(n+1) = O(logn)
AVL树的描述
一般用链表描述。但是,为了简化插入和删除,为每个节点增加一个平衡因子 bf。
节点x的平衡因子bf(x)定义为:x的左子树高度 - x的右子树高度
bf可以取值为:-1,0,1
AVL搜索树的搜索O(logn)
与二叉搜索树的搜索一模一样,见数据结构之二叉搜索树
AVL搜索树的插入O(logn)
利用二叉搜索树的插入算法,但会出现一个问题,就是插入之后的树有可能不是一个AVL搜索树。所以,我们的目的是将插入后的树变成一棵AVL搜索树(若插入后不是AVL搜索树)。
插入操作导致不平衡的几种现象:
1)不平衡树中的平衡因子值为:-2,-1,0,1,2
2)平衡因子为2的节点在插入之前为1,平衡因子为-2的节点在插入之前为-1
3)从根到新插入节点的路径上,只有经过的节点的平衡因子在插入后会改变
4)假设A是新插入节点的最近的祖先,它的平衡因子是-2或2,那么,在插入前从A到新插入的节点的路径上,所有节点的平衡因子是0
使树的平衡遭到破坏的唯一一种情形,是插入过程中使得平衡因子bf(X)的值由-1变为-2或者由1变为2。对于后者,插入操作一定是发生在X的左子树中。
当节点A已经确定下来之后,A的不平衡性可归类为L型不平衡(新插入节点在A的左子树中)和R型不平衡。通过确定A的哪一孙子节点在通往新插入节点的路径上,可以进一步细分不平衡类型。这种孙子节点一定存在,因为A的平衡因子是-2或2.根据上面的方法,A节点的不平衡类型可以细分为LL(新插入节点在A节点的左子树的左子树中),LR(新插入节点在A节点的左子树的右子树中),RL,RR四种类型。
为了恢复平衡,针对四种类型,分别有以下旋转方法,使得旋转之后,恢复为一棵AVL搜索树。
LL旋转
由于A的平衡因子改变了,所以处于从B到新插入节点途中的B’的所有节点的平衡因子都将改变,其他节点的平衡因子与旋转前保持一致。重新平衡的树确实是一棵二叉搜索树。
LR旋转
插入操作发生在B的右子树,C存在,但是CL或者CR有可能为空,故bf(C)不确定。重新整理后,bf(B)和bf(A)的值取决于bf(C)在插入之后重新整理之前的值。整理后仍然是一棵二叉搜索树。由于插入前和旋转后的子树高度相同,所以它们的祖先的平衡因子不变。
RL旋转
RR旋转
RL旋转、RR旋转与上面讨论的情形是对称的。把矫正LL和RR型不平衡成为单旋转,把矫正LR和RL型不平衡称为双旋转。对LR型的不平衡的转换可以看作是RR旋转后的LL旋转,而对RL型的旋转可以看作是LL旋转之后的RR旋转。
AVL搜索树的删除O(logn)
首先用二叉搜索树的删除,然后恢复其平衡。设q是被删除节点的父节点,如果删除此元素之后,其左子树中的最大元素或右子树中的最小元素补过来了,那么q就是补过来的那个元素之前的父节点。
删除之后的现象:
1)如果q的新平衡因子是0,那么它的高度减少了1,并且需要改变其父节点和其他某些祖先的平衡因子。
2)如果q的新平衡因子是-1或1,那么它的高度与删除前相同,无需改变其父节点和其祖先的平衡因子。
3)如果q的新平衡因子是-2或2,那么树 在q节点不平衡。
由于平衡因子可以沿着从q到根节点的路径改变,所以途中节点的平衡因子有可能为-2或2,设A是第一个这样的节点。如果删除发生在A的左子树,那么不平衡是L型的,反之R型。如果删除之后bf(A)=2,那么删除前bf(A)一定是1。因此,A有一颗以B为根的左子树。根据bf(B)的值,可以把R型分为R0,R1,R-1类型。同理,L型也分为L0,L1,L-1类型。
至于如何旋转,类似于插入的旋转。
R0,R1需要右旋,但是这两种旋转之后A,B的新平衡因子不同;R-1需要先左旋后右旋,类似于LR旋转。
不同于插入操作,再删除操作之后,一次旋转可能还为u发达到平衡必须继续检查到达根节点路径上的节点,所需要的旋转次数为O(logn)。
推荐一篇博客,本人觉得对于如何旋转理解很有帮助AVL树的插入与删除