AVL树
- AVL树是带平衡条件的平衡二叉树
- 平衡条件:其每个节点的左子树和右子树的高度
- 这是一个AVL树
- 这就不是一个AVL树了
解决AVL树插入节点后破坏平衡条件
- 通过旋转方式改变AVL树的节点,达到平衡。
- 我们把必须重新平衡的节点叫做a,由于任意节点最多有两个儿子,因此出现高度不平衡就需要a点的两棵子树的高度差2,容易看出这种不平衡可能出现在下面四种情况中:
- 1、对a的左儿子的左子树进行一次插入 – (左 - 左)
- 2、对a的左儿子的右子树进行一次插入 – (左 - 右)
- 3、对a的右儿子的左子树进行一次插入 – (右 - 左)
- 4、对a的右儿子的右子树进行一次插入 – (右 - 右)
其中对于 1和4 我们采用单旋转,对于2和3我们采用双旋转来解决这个问题,实现新的AVL树
单旋转
插入节点6之后整个avl树破坏了平衡节点,在节点8处。这里的a就是8。这里属于(左- 左)情况,我们将8节点定义为k1,将7节点定义为k2,现在将k2放到k1的位置,解决了。
- 同理对于情形4也是同理
- 小结:我们将a节点设为k1,将a节点的下一个节点设为k2,之后将k2的位置放到k1后,重新节点分配就形成了新的AVL树,这里还有一个小知识点是当插入的节点数值不在k1和k2之间时,那么就会是情形1和情形4,同时用单旋转解决。
双旋转
- 这里用于解决情形2和情形3(也就是左-右和右-左)。双旋转实际上就是两次单旋转
- 这里将a(7)设为k1,k1下面的点为k3,k3下面的点为k2。将k2的节点放到a的位置上,其他节点重新排布。造成需要双旋转的节点的数值一定在k1和k3之间
- 在这里7是k1,16是k3,15是k2。
- 按照上述步骤解决,这个是右-左,同理左-右也是一样的。
实现代码
AVL树的数据结构
- 这是一个内部类
public static class AvlNode<T>{
AvlNode node;
AvlNode<T> rnode;
AvlNode<T> lnode;
Integer height;
AvlNode(AvlNode node){
this(node,null,null);
}
AvlNode(AvlNode node,AvlNode<T> lnode,AvlNode<T> rnode){
this.node = node;
this.rnode = rnode;
this.lnode = lnode;
}
}
单旋转
- 这里面相当于是传入节点的左边部分放到上面,交换节点。
private AvlNode<T> rotateWithLeftChild(AvlNode<T> k2){
AvlNode<T> k1 = k2.lnode;
k2.lnode = k1.rnode;
k1.rnode = k2;
k2.height = Math.max(height(k2.lnode),height(k2.rnode))+1;
k1.height = Math.max(height(k1.lnode),height(k1.rnode))+1;
return k1;
}
- 同理,右边旋转
private AvlNode<T> rotateWithRightChild(AvlNode<T> k2){
AvlNode<t> k1 = k2.rnode;
k2.rnode = k1.lnode;
k1.lnode = k2;
k2.height = Math.max(height(k2.lnode),height(k2.rnode))+1;
k1.height = Math.max(height(k1.lnode),height(k1.rnode))+1;
return k1;
}
计算高度
public Integer height(AvlNode<T> t ){
return t == null?-1:t.height;
}
双旋转
private AvlNode<T> doubleWithLeftChild(AvlNode<T> k3){
k3.lnode = rotateWithRightChild(k3.lnode);
return rotateWithLeftChild(k3);
}
private AvlNode<T> doubleWithRightChild(AvlNode<T> k3){
k3.lnode = rotateWithLeftChild(k3.lnode);
return rotateWithRightChild(k3)
}