定义及原理
平衡二叉树(Balanced Binary Tree)是二叉查找树的一个进化体,也是第一个引入平衡概念的二叉树。1962年,G.M. Adelson-Velsky 和 E.M. Landis发明了这棵树,所以它又叫AVL树。具有以下性质:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。 最小平衡二叉树的节点总数的公式如下 :
F(n)=F(n-1)+F(n-2)+1
这个类似于一个递归的数列,可以参考Fibonacci(斐波那契)数列,1是根节点,F(n-1)是左子树的节点数量,F(n-2)是右子树的节点数量。
为何需要平衡二叉树
一般的二叉搜索树(Binary Search Tree),在某些极端的情况下(eg:插入的序列是有序的),将退化成近似链表,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作(insert、delete)之后,就会造成树的平衡性受到破坏,提高它的操作的时间复杂度。而平衡二叉树在每次操作(insert、delete)之后,都会进行自平衡(通过旋转算法实现),这个方案很好的解决了二叉查找树退化成链表的问题,把插入,查找,删除的时间复杂度最好情况和最坏情况都维持在O(logN)。但是平衡二叉树的频繁旋转会使插入和删除牺牲掉O(logN)左右的(需要反向查询以确定采用何种旋转算法),不过相对二叉搜索树来说,其查找的时间复杂度稳定了很多。
旋转问题
对于一个平衡的节点,由于任意节点最多有两个儿子,因此首次出现高度不平衡时,此节点的两颗子树的高度差的绝对值等于2.容易看出,这种不平衡出现在下面四种情况:
-
节点6的左子树节点3的高度与右子树节点7的高度之差大于1,左子树节点3的左子树节点1的高度大于右子树节点4,这种情况为左左。
-
节点6的左子树节点2的高度与右子树节点7的高度之差大于1,左子树节点2的左子树节点1的高度小于右子树节点4,这种情况为左右。
-
节点2的左子树节点1的高度与右子树节点5的高度之差小于-1,右子树节点5的左子树节点3的高度大于右子树节点6,这种情况为右左。
-
节点2的左子树节点1的高度与右子树节点4的高度之差小于-1,右子树节点4的左子树节点3的高度小于右子树节点6,这种情况为右右。
从图2中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。
旋转算法
-
单旋转是针对于左左和右右这两种情况的解决方案,这两种情况是对称的,只要解决了左左这种情况,右右就很好办了。下图是左左情况的解决方案,节点a不满足平衡特性,因为它的左子树b比右子树f深2层,而且b子树中,更深的一层的是c的左子树d子树,所以属于左左情况。
为使树恢复平衡,我们把节点b变成这棵树的根节点,因为节点a的值大于节点b的值,把节点a置于节点b的右子树上,而原本节点b的右子树e的值小于节点a的值,就把节点e置于节点a的左子树上,这样既满足了二叉查找树的性质,又满足了平衡二叉树的性质。由于左左和右右是对称的,因此在此不做赘述。 -
双旋转是针对于左右和右左这两种情况的解决方案,这两种情况也是对称的,因此只要解决了左右这种情况,右左就很好解决了。下图是左右情况的解决方案,节点a不满足平衡特性,因为它的左子树b比右子树f深2层,而且b子树中,更深的一层的是b的右子树e子树,所以属于左右情况。
为使树恢复平衡,第一步,我们需要把左右情况变成单旋转的左左情况,把节点e向左旋转,因为节点b的值小于节点e的值,把节点b置于节点e的左子树上,而原本节点e的右子树f的值大于于节点b的值,就把节点f置于节点b的右子树上,这样就变成了左左的情况;第二步,根据左左情况如上面的步骤进行右旋,使其满足了平衡二叉树的性质。由于左右和右左是对称的,因此在此不做赘述
相关代码
public final class BalanceBinaryTree<T extends Comparable> {
private BalanceBinaryTreeNode<T> root