AVL树
1 描述
AVL树是在儿叉搜索树的基础上衍生出来的数据结构。在儿叉搜索树中,高度取决于结点数据的大小的排序和插入顺序,在某些情况下,儿叉排序树的高度非常高,甚至退化成链表,使得其查找效率低下。因此,AVL树引入“平衡因子”的概念,平衡因子=左子树高度-右子树的高度。AVL树规定平衡因子的绝对值不大于1来保证二叉树的平衡。
关于二叉排序树的描述参考:点击跳转
2 操作流程
2.1 添加结点情况
添加一个结点对其父节点影响不会导致其失衡,而可能导致祖父结点失衡(可能所有祖父结点),因此只需让高度最低的失衡结点恢复平衡即可使得整棵树平衡,旋转操作次数为O(1)。我们对平衡二叉树失衡情况进行归类,可分为如下几种情况。
- LL单旋
下图所示为LL失衡情况,失衡结点由g结点的left结点的left结点所导致(标红部分为新加入的结点),可通过获取左子树和右子树哪个高进行情况判定。
对此,对g结点进行右旋转使之平衡。主要操作伪代码:
g.left = p.right;
p.right = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.left.parent = g;
- 双旋
下图所示LR失衡情况,失衡结点为g结点的left结点的right结点。
双旋调整即对其进行两次调整。先将p结点左旋,得到LL情况,进而对g结点右旋。主要操作伪代码:
//先p左旋
p.right = n.left;
n.left = p;
n.parent = p.parent;
n.parent.left = n;
p.parent = n;
p.right.parent = p;
//后g右旋,操作流程同LL部分
g.left = p.right;
p.right = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.left.parent = g;
- RR单旋
RR单旋的情况和LL单旋的情况相反,只需对其进行相反的操作即可。
其主要操作伪代码:
g.right = p.left;
p.left = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.right.parent = g;
- RL双旋
RL双旋的情况和LR双旋的情况相反。
//先p左旋
p.left = n.right;
n.right = p;
n.parent = p.parent;
n.parent.right = n;
p.parent = n;
p.right.parent = p;
//后g右旋,操作流程同LL部分
g.right = p.left;
p.left = g;
p.parent = g.parent;
if g is left child then set p.parent.left = p else set p.parent.right = p;
g.parent = p;
g.right.parent = g;
2.2 删除结点的情况
删除结点可能导致父节点或祖父结点失衡(只有1各位结点会失衡),但对其恢复平衡后,可能使得子树高度降低,延续到祖先结点也可能产生失衡的情况,旋转操作次数为O(logn)。对删除结点进行归类,可分为如下几种情况。
-
LL单旋
删除T3下的某个节点,使得T3树高度-1,g结点失衡,g的左子树较高,通过对其子树的高度进行判定可知为LL情况,进而对其进行右旋转。主要旋转操作伪代码同添加的LR情况相同,但在旋转后,子树高度降低,可能导致祖父结点失衡,需要进一步对祖父结点进行失衡判定和调整。 -
LR双旋
同上,通过对其左右子树的高度进行判定为LR情况,进而对其旋转,操作类似于添加LR操作。
-
RR单旋
-
RL双旋
3 算法实现
主要包括添加,删除,旋转,更新高度等。
3.1 旋转树类
我们对旋转操作进行抽象成一个类,以便后续为红黑树准备。
public abstract class BBSTree<E> extends BSTreeImpl<E>{
public BBSTree(){
this(null);
}
public BBSTree(Comparator<E> comparator){
super(comparator);
}
/**
* 左旋
* @param node
*/
protected void rotateLeft(Node<E> grand){
Node<E> parent = grand.right;
Node<E> child = parent.left;
grand.right = child;
parent.left = grand;
parent.parent = grand.parent;
if(grand.isLeftChild()){
grand.parent.left = parent;
}else if(grand.isRightChild()){
grand.parent.right = parent;
}else{ //要删除的结点为根结点
root = parent;
}
if(child != null)child.parent = grand;
grand.parent = parent;
afterRotateLeft(grand, parent, child);
}
/**
* 右旋
* @param node
*/
protected void rotateRight(Node<E> grand){
Node<E> parent = grand.left;
Node<E> child = parent.right;
parent.right = grand;
grand.left = child;
parent.parent = grand.parent;
if(grand.isLeftChild()){
grand.parent.left = parent;
}else if(grand.isRightChild()){
grand.parent.right = parent;
}else{ //要删除的结点为根结点
root = parent;
}
if(child != null) child.parent = grand;
grand.parent = parent;
afterRotateRight(grand, parent, child);
}
protected void afterRotateLeft(Node<E> grand,Node<E> parent,Node<E> child){}
protected void afterRotateRight(Node<E> grand,Node<E> parent,Node<E> child){}
}
3.2 结点设计
对AVL树使用了平衡因子的概念,为了节省开销,不每次通过递归法获取高度,因此需要对其增加高度属性。
protected static class AVLNode<E> extends Node<E>{
public int height = 1;
public AVLNode(E element, Node<E> parent) {
super(element, parent);
}
/**
* 获取平衡因子
* @return
*/
public int balanceFactor(){
int leftHeight = left == null?0:((AVLNode<E>)left).height;
int rightHeight = right == null?0:((AVLNode<E>)right).height;
return leftHeight - rightHeight;
}
public void updateHeight(){
int leftHeight = left == null?0:((AVLNode<E>)left).height;
int rightHeight = right == null?0:((AVLNode<E>)right).height;
this.height = (leftHeight > rightHeight?leftHeight:rightHeight) + 1;
}
/**
* 获取左右子树中高的结点
* @return
*/
public AVLNode<E> tallerChild(){
int leftHeight = left == null?0:((AVLNode<E>)left).height;
int rightHeight = right == null?0:((AVLNode<E>)right).height;
return (AVLNode<E>)(leftHeight > rightHeight?left:right);
}
}
3.3 类设计
public class AVLTree<E> extends BBSTree<E>{
public AVLTree(){
this(null);
}
public AVLTree(Comparator<E> comparator){
super(comparator);
}
@Override
protected Node<E> createNode(E element,Node<E> parent) {
return new AVLNode<>(element, parent);
}
/**
* 是否平衡
* @param node
* @return
*/
private boolean isBanlance(Node<E> node){
return Math.abs(((AVLNode<E>)node).balanceFactor()) <= 1;
}
/**
* 更新高度
* @param node
*/
private void updateHeight(Node<E> node){
AVLNode<E> avlNode = (AVLNode<E>)node;
avlNode.updateHeight();
}
/**
* 恢复平衡,不平衡的结点一定是添加后的祖先结点
* @param grand
*/
private void rebalance(Node<E> grand){
Node<E> parent = ((AVLNode<E>)grand).tallerChild();
Node<E> node = ((AVLNode<E>)parent).tallerChild();
if(parent.isLeftChild()){
if(node.isLeftChild()){ //LL
rotateRight(grand);
}else{ //LR
rotateLeft(parent);
rotateRight(grand);
}
}else{
if(node.isLeftChild()){ //RL
rotateRight(parent);
rotateLeft(grand);
}else{ //RR
rotateLeft(grand);
}
}
}
}
3.4 添加结点
@Override
public void add(E element) {
super.add(element);
}
@Override
protected void afterAdd(Node<E> node){
while((node = node.parent) != null){
if(isBanlance(node)){
//更新高度
updateHeight(node);
}else{
//恢复平衡
rebalance(node);
break; //调整后直接break,调整后的子树和添加结点前子树高度不变, 因此上层结点不会失衡
}
}
}
3.5 删除结点
@Override
public void remove(E element) {
super.remove(element);
}
@Override
protected void afterRemove(Node<E> node,Node<E> replacement){
while((node = node.parent) != null){
if(isBanlance(node)){
updateHeight(node);
}else{
rebalance(node); //这里不需要break,因为在调整后,可能会导致上层祖父结点失衡
}
}
}
3.6 旋转后的操作
旋转后树高度有变化,因此需要对其进行调整。
@Override
protected void afterRotateLeft(Node<E> grand,Node<E> parent,Node<E> child) {
updateHeight(grand);
updateHeight(parent);
}
@Override
protected void afterRotateRight(Node<E> grand,Node<E> parent,Node<E> child) {
updateHeight(grand);
updateHeight(parent);
}
4 补充
对旋转操作进行汇总。
发现旋转后的操作abcdefg子树按顺序排列,进而可对旋转操作进行统一。(另外一种旋转思路)
@SuppressWarnings("unused")
private void rebalance2(Node<E> grand){
Node<E> parent = ((AVLNode<E>)grand).tallerChild();
Node<E> node = ((AVLNode<E>)parent).tallerChild();
if(parent.isLeftChild()){
if(node.isLeftChild()){ //LL
rotate(grand, node.left, node, node.right, parent, parent.right, grand, grand.right);
}else{ //LR
rotate(grand, parent.left, parent, node.left, node, node.right, grand, grand.right);
}
}else{
if(node.isLeftChild()){ //RL
rotate(grand, grand.left, grand, node.left, node, node.right, parent, parent.right);
}else{ //RR
rotate(grand, grand.left, grand, parent.left, parent, node.left, node, node.right);
}
}
}
private void rotate(Node<E> r, //子树的根结点
Node<E> a,Node<E> b,Node<E> c,
Node<E> d,
Node<E> e,Node<E> f,Node<E> g){
d.parent = r.parent;
if(r.isLeftChild()){
r.parent.left = d;
}else if(r.isRightChild()){
r.parent.right = d;
}else{
root = d;
}
b.left = a;
if(a != null)a.parent = b;
b.right = c;
if(c != null)c.parent = b;
updateHeight(b);
f.left = e;
if(e != null)a.parent = f;
f.right = g;
if(g != null)g.parent = f;
updateHeight(f);
d.left = b;
b.parent = d;
d.right = f;
f.parent = d;
updateHeight(d);
}
5 完整代码
完整代码在我的Github:点击跳转