AVL树
AVL树是带有平衡条件的二叉查找树。一颗AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树
当进行插入操作时,我们需要更新通向根节点路径上那些节点的所有平衡信息,而插入操作隐含着困难的原因在于,插入一个节点可能破坏AVL树的特性。如果发生这种情况,那么就要在考虑这一步插入完成之前恢复平衡的性质。事实上,这总可以通过对树进行简单的修正来做到,我们称其为选择
我们把必须重新平衡的节点叫做a。由于任意节点最多有两个儿子,因此出现高度不平衡就需要a点的两颗子树的高度差2。容易看出有下面4中情况:
- 对a的左儿子的左子树进行一次插入
- 对a的左儿子的右子树进行一次插入
- 对a的右儿子的右子树进行一次插入
对a的右儿子的左子树进行一次插入
因为情况1和4,以及2和3是两组关于a点的镜像对称。因此,理论上只有两种情况,当然从编程的角度来看还是四种情况
第一种情况是插入发生在“外边”的情况(即左-左的情况或者右-右的情况),该情况通过对树的一次单旋转而完成调整。
第二种情况是插入发生在“内部”的情形(即左-右的情况或者右-左的情况),该情况通过稍微复杂些的双旋转来处理。
private static class AvlNode<AnyType>{
AvlNode(AnyType theElement)
{this(theElement, null, null);}
AvlNode(AnyType theElement, AvlNode<AnyType> lt, AvlNode<AnyType rt)
{element = theElement; left = lt; right = rt; height = 0;}
AnyType element;
AvlNode<AnyType> left;
AvlNode<AnyType> right;
int height;
}
单旋转
为使树恢复平衡,我们把X上移一层,并把Z下移一层。即搜寻到不符合AVL的性质(左右子树高度差为1)节点k2,使其左子树k1上升,并且k2自身下降,并且k1的右子树称为为k2的左子树。
private AvlNode<AnyType> rotateWithLeftChild(AvlNode<AnyType k2){
AvlNode<AnyType> k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
k1.height = Math.max(height(k1.left), height(k2.right)) + 1;
return k1;
}
双旋转
节点k2上升至k1,k1作为k2的左子树且获得k2的左子树,k3作为k2的右子树且获得k2的右子树。相当于进行了两次单旋转,第一为右旋转,第二次为左旋转,所以称为双旋转。
private AvlNode<AnyType> doubleWithLeftChild(AvlNode<AvlType> k3){
k3.left = rotateWithRightChild(k3.left);
return rotateWithLeftChild(k3);
}