在引入AVL树之前先讨论一下普通二叉树的缺点:
对于一颗高度为的二叉树而言,其结点数量为至-1,同理可得,当一颗二叉树结点为时,其高度为+1至,当二叉树完全失去平衡时,即节点数=高度,此时查找一个结点的算法复杂度为;
此时我们对于普通二叉树的缺点已经有所了解了,此时如何寻找解决策略呢?也就是如何保持二叉树的平衡?
平衡因子:在此之前我们需要理解平衡因子的概念,我们为每一个结点加入一个高度的属性,定义空结点高度一律为0,那么一个结点的平衡因子等于该结点右子结点的高度减去该结点左子结点的高度(相减的顺序没有强制性要求,如果改变则只需要改变相应的偏重策略),如果平衡因子为-1,那么即为左偏重,1为右偏重,0为平衡,除此之外即为失衡,需要旋转操作来保持平衡;
AVL树的旋转操作:每次插入、删除一个结点之后都会根据平衡因子判断是否失衡,且失衡情况最多只有四种,左左、右右、左右、右左。左左旋转操作的前提的新插入或者删除的结点是失衡结点的左子结点方向的左子节点方向(最多相隔两个结点),其他情况类推即可。
下面具体讨论左左旋转,见下图:
简单的左左旋转
P代表平衡因子,New代表新插入的结点;这是一个情况较为简单的左左旋转,接下来考虑一个复杂些的左左旋转,如下图:
情况较为复杂的左左旋转
P和New结点意义同上,结点A、B、C满足A<B<C,通过简单的观察可以发现左左旋转的算法简要描述与步骤是:
1.K2的右子结点赋给K1的左子结点;
2.K1赋给K2的右子结点;
3.调节高度(注意观察图片,New、A、B、C高度均不变,只需要调节K1和K2);
具体算法如下:
/**
* LL:左单旋转(即左左旋转,左左旋转较为通俗,而左单旋转是标准称呼)
* 传入失衡之后的失衡结点,返回调节之后的平衡结点
*/
private Node<E> llRotation(Node<E> k1)
{
// 定义K2结点
Node<E> k2 = k1.left;
// K2的右子结点赋给K1的左子结点
k1.left = k2.right;
// k1赋给K2的右子结点
k2.right = k1;
// 调节K1和K2的高度
k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
k2.height = Math.max(height(k2.left), k1.height) + 1;
return k2;
}
下面具体讨论左右旋转,如下图:
简单的左右旋转
此时我们可以发现命名为左左旋转、左右旋转是再合适不过了,其中New、K1、K2的意义和上面是一致的,接下类继续看较为复杂的左右旋转,如下图:
复杂情况的左右旋转
与左左旋转类似,通过观察图像可以发现左右旋转算法的简要概述和步骤为:
1.把New结点赋给K1的左节点;
2.把K1赋给K3的右结点;
3.把K2赋给K3的左节点;
4.调节高度(只有K1、K3的高度发生了变化,故只需要调节这两个结点的高度);
具体算法如下(左右旋转标准称呼为左双旋转,需要借助右右旋转和左左旋转):
/*
* LR:左双旋转(即左右旋转)
* 参数:失衡的结点
* 返回值:旋转后的结点
*/
private Node<E> lrRotation(Node<E> k3)
{
k3.left = rrRotation(k3.left);
return llRotation(k3);
}
右右旋转和右左旋转是分别关于左左旋转和左右旋转对称的,通俗的说,将算法中的K1和K2、Left和Rigth相互转换即可;
实现AVL树的基本程序如下:
package A02.Tree;
public class AVLTree<E extends Comparable<? super E>>
{
public static void main(String[] args)
{
AVLTree<Integer> tree = new AVLTree<>();
tree.insert(6);
tree.insert(5);
tree.insert(4);
tree.insert(8);
//tree.remove(5);
//tree.remove(6);
//tree.remove(9);
tree.remove(6);
tree.remove(5);
tree.remove(6);
tree.disPlay();
System.out.println("平衡因子=" + tree.getBalance(tree.getBalance(0)));
System.out.println("平衡因子=" + tree.getBalance(tree.getBalance(5)));
System.out.println("平衡因子=" + tree.getBalance(tree.getBalance(-61)));
System.out.println("最小值=" + tree.getMin(tree.root));
}
private Node<E> root;
// 获取一个节点的高度
private int height(Node<E> root)
{
return root == null ? 0 : root.height;
}
public void insert(E data)
{
root = insert(data, root);
}
// LL:左单旋转
private Node<E> llRotation(Node<E> k2)
{
Node<E> 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), k2.height) + 1;
return k1;
}
// RR:右单旋转
private Node<E> rrRotation(Node<E> k1)
{
Node<E> k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
k2.height = Math.max(height(k2.right), k1.height) + 1;
return k2;
}
/*
* LR:左双旋转
*/
private Node<E> lrRotation(Node<E> k3)
{
k3.left = rrRotation(k3.left);
return llRotation(k3);
}
/*
* RL:右双旋转
*/
private Node<E> rlRotation(Node<E> k1)
{
k1.right = llRotation(k1.right);
return rrRotation(k1);
}
/*
* 插入节点
*/
private Node<E> insert(E data, Node<E> root)
{
if (root == null)
root = new Node<E>(data, 0, null, null);
else
{
int result = data.compareTo(root.data);
if (result < 0)
{
root.left = insert(data, root.left);
// 插入节点后,若AVL树失去平衡,则进行相应的调节。
if (height(root.left) - height(root.right) == 2)
if (data.compareTo(root.left.data) < 0)
root = llRotation(root);
else
root = lrRotation(root);
} else if (result > 0)
{
root.right = insert(data, root.right);
// 插入节点后,若AVL树失去平衡,则进行相应的调节。
if (height(root.right) - height(root.left) == 2)
if (data.compareTo(root.right.data) > 0)
root = rrRotation(root);
else
root = rlRotation(root);
} else
System.out.println("同值");
}
root.height = Math.max(height(root.left), height(root.right)) + 1;
return root;
}
// 中序遍历
public void disPlay()
{
if (root != null)
disPlay(root);
}
private void disPlay(Node<E> root)
{
if (root.left != null)
disPlay(root.left);
System.out.println("data=" + root.data + ",height=" + root.height);
if (root.right != null)
disPlay(root.right);
}
public boolean find(E data)
{
if (root == null)
return false;
return find(root, data) != null;
}
private Node<E> find(Node<E> root, E data)
{
if (root == null)
return null;
int result = data.compareTo(root.data);
if (result > 0)
return find(root.right, data);
else if (result < 0)
return find(root.left, data);
else
return root;
}
public int getBalance(E data)
{
return find(data) ? getBalance(find(root, data)) : -1;
}
private int getBalance(Node<E> root)
{
if (root.left != null && root.right != null)
return root.right.height - root.left.height;
else
return root.left == null ? 1 : -1;
}
public void remove(E data)
{
if (find(root, data) != null)
root = remove(root, data);
}
private Node<E> remove(Node<E> tree, E data)
{
if (tree == null)
return null;
int result = data.compareTo(tree.data);
if (result < 0)
{
tree.left = remove(tree.left, data);
// 删除后判断是否失衡
if (height(tree.right) - height(tree.left) == 2)
{
Node<E> r = tree.right;
if (height(r.left) > height(r.right))
tree = rlRotation(tree);
else
tree = rrRotation(tree);
}
} else if (result > 0)
{
tree.right = remove(tree.right, data);
// 删除后判断是否失衡
if (height(tree.left) - height(tree.right) == 2)
{
Node<E> l = tree.left;
if (height(l.right) > height(l.left))
tree = lrRotation(tree);
else
tree = llRotation(tree);
}
} else
{
// 删除结点是否存在左右子节点
if ((tree.left != null) && (tree.right != null))
{
// 与普通二叉树的删除有细微的差别
// 普通二叉树:无论如何,都是由其右子结点中的最大值顶替删除的结点
// AVL树:为了保持平衡,由高度较高的子结点的最大值顶替删除的结点
if (height(tree.left) > height(tree.right))
{
E max = getMax(tree.left);
tree.data = max;
tree.left = remove(tree.left, max);
} else
{
E min = getMax(tree.right);
tree.data = min;
tree.right = remove(tree.right, min);
}
} else
tree = (tree.left != null) ? tree.left : tree.right;
}
return tree;
}
private E getMin(Node<E> root)
{
if (root == null)
return null;
if (root.left != null)
return getMin(root.left);
else
return root.data;
}
private E getMax(Node<E> root)
{
if (root == null)
return null;
if (root.right != null)
return getMin(root.right);
else
return root.data;
}
private class Node<E>
{
E data;
int height;
Node<E> left;
Node<E> right;
public Node(E data, int height, Node<E> left, Node<E> right)
{
this.data = data;
this.height = height;
this.left = left;
this.right = right;
}
}
}