1 平衡树和AVL树
- 平衡树(Balance Tree,BT) :任意节点的子树的高度差都小于等于1。常见的符合平衡树的有:B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等;
- AVL树:首先是一棵二叉搜索树,其次是高度平衡的二叉树,树中任意节点的两个子树的高度差都小于等于1;
2 结点
-
AVLTree是AVL树对应的类,而AVLTreeNode是AVL树节点,它是AVLTree的内部类;
-
AVLTree包含了AVL树的根节点,AVL树的基本操作也定义在AVLTree中;
-
AVLTreeNode包括以下几部分:
(1)key – 关键字,用来对AVL树的节点进行排序;
(2)left – 左子结点;
(3)right – 右子结点;
(4)height – 高度;
3 旋转
- 在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡;
- 失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RL(右左),RR(右右);
- LL:也称左左,插入或删除一个结点后,根结点的左子树高度-右子树高度=2,且左子树的左子树高度-左子树的右子树高度=1;
- LR:也称左右,插入或删除一个结点后,根结点的左子树高度-右子树高度=2,且左子树的右子树高度-左子树的左子树高度=1;
- RL:也称右左,插入或删除一个结点后,根结点的右子树高度-左子树高度=2,且右子树的左子树高度-右子树的右子树高度=1;
- RR:也称右右,插入或删除一个结点后,根结点的右子树高度-左子树高度=2,且右子树的右子树高度-右子树的左子树高度=1;
3.1 LL的旋转
LL失去平衡的情况,可以通过一次右旋转让AVL树恢复平衡。如下图:
3.2 RR的旋转
RR失去平衡的情况,可以通过一次左旋转让AVL树恢复平衡。如下图:
3.3 LR的旋转
LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡,即一次左子节点的左旋转+一次右旋转。如下图:
3.4 RL的旋转
RL失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡,即一次右子节点的右旋转+一次左旋转。如下图:
4 完整代码
public class AVLTree<T extends Comparable<T>> {
private AVLTreeNode<T> mRoot; // 根结点
// AVL树的节点(内部类)
private class AVLTreeNode<T extends Comparable<T>> {
T key; // 键值
AVLTreeNode<T> left; // 左子结点
AVLTreeNode<T> right; // 右子结点
int height; // 高度
public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
this.key = key;
this.left = left;
this.right = right;
this.height = 0;
}
}
// 获取AVL树高
public int height() {
return height(mRoot);
}
// 获取当前结点为根结点对应的树高
private int height(AVLTreeNode<T> tree) {
if (tree != null) {
return tree.height;
}
return 0;
}
// 获取两值的最大值
private int max(int a, int b) {
return a > b ? a : b;
}
// 前序遍历AVL树
public void preOrder() {
preOrder(mRoot);
}
private void preOrder(AVLTreeNode<T> tree) {
if(tree != null) {
System.out.print(tree.key + " ");
preOrder(tree.left);
preOrder(tree.right);
}
}
// 中序遍历AVL树
public void inOrder() {
inOrder(mRoot);
}
private void inOrder(AVLTreeNode<T> tree) {
if(tree != null)
{
inOrder(tree.left);
System.out.print(tree.key + " ");
inOrder(tree.right);
}
}
// 后序遍历AVL树
public void postOrder() {
postOrder(mRoot);
}
private void postOrder(AVLTreeNode<T> tree) {
if(tree != null) {
postOrder(tree.left);
postOrder(tree.right);
System.out.print(tree.key + " ");
}
}
/*
* (递归实现)查找"AVL树x"中键值为key的节点
*/
public AVLTreeNode<T> recursiveSearch(T key) {
return recursiveSearch(mRoot, key);
}
private AVLTreeNode<T> recursiveSearch(AVLTreeNode<T> x, T key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp < 0) {
return recursiveSearch(x.left, key);
}
else if (cmp > 0) {
return recursiveSearch(x.right, key);
}
else {
return x;
}
}
/*
* (迭代实现)查找"AVL树x"中键值为key的节点
*/
public AVLTreeNode<T> iterativeSearch(T key) {
return iterativeSearch(mRoot, key);
}
private AVLTreeNode<T> iterativeSearch(AVLTreeNode<T> x, T key) {
while (x!=null) {
int cmp = key.compareTo(x.key);
if (cmp < 0)
x = x.left;
else if (cmp > 0)
x = x.right;
else
return x;
}
return x;
}
/*
* 求最小结点的键值
*/
public T minimum() {
AVLTreeNode<T> p = minimum(mRoot);
if (p != null)
return p.key;
return null;
}
private AVLTreeNode<T> minimum(AVLTreeNode<T> tree) {
if (tree == null)
return null;
while(tree.left != null)
tree = tree.left;
return tree;
}
/*
* 求最大结点的键值
*/
public T maximum() {
AVLTreeNode<T> p = maximum(mRoot);
if (p != null)
return p.key;
return null;
}
private AVLTreeNode<T> maximum(AVLTreeNode<T> tree) {
if (tree == null)
return null;
while(tree.right != null)
tree = tree.right;
return tree;
}
/*
* LL:左左对应的情况(右旋转)。
*/
private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k1) {
AVLTreeNode<T> k2;
k2 = k1.left;
k1.left = k2.right;
k2.right = k1;
k1.height = max(height(k1.left), height(k1.right)) + 1;
k2.height = max(height(k2.left), height(k2.right)) + 1;
return k2;
}
/*
* RR:右右对应的情况(左旋转)。
*/
private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
AVLTreeNode<T> k2;
k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
k1.height = max(height(k1.left), height(k1.right)) + 1;
k2.height = max(height(k2.left), height(k2.right)) + 1;
return k2;
}
/*
* LR:左右对应的情况(先左子节点左旋后右旋)。
*/
private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
k3.left = rightRightRotation(k3.left);
return leftLeftRotation(k3);
}
/*
* RL:右左对应的情况(先右子节点右旋后左旋)。
*/
private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
k1.right = leftLeftRotation(k1.right);
return rightRightRotation(k1);
}
/*
* 将新结点插入到AVL树中
*/
public void insert(T key) {
mRoot = insert(mRoot, key);
}
private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
if (tree == null) {
// 新建节点
tree = new AVLTreeNode<T>(key, null, null);
} else {
int cmp = key.compareTo(tree.key);
if (cmp < 0) { // 将key插入到tree的左子树
tree.left = insert(tree.left, key);
// 插入节点后,若AVL树失去平衡,则进行相应的调节
if (height(tree.left) - height(tree.right) == 2) {
if (key.compareTo(tree.left.key) < 0)
tree = leftLeftRotation(tree);
else
tree = leftRightRotation(tree);
}
}
else if (cmp > 0) { // 将key插入到tree的右子树
tree.right = insert(tree.right, key);
// 插入节点后,若AVL树失去平衡,则进行相应的调节
if (height(tree.right) - height(tree.left) == 2) {
if (key.compareTo(tree.right.key) > 0)
tree = rightRightRotation(tree);
else
tree = rightLeftRotation(tree);
}
}
else { // cmp == 0
System.out.println("添加失败:不允许添加相同的节点!");
}
}
tree.height = max(height(tree.left), height(tree.right)) + 1;
return tree;
}
/*
* 删除键值为k的结点
*/
public void remove(T key) {
AVLTreeNode<T> z = recursiveSearch(mRoot, key);
if (z != null)
mRoot = remove(mRoot, z);
}
private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
int cmp = z.key.compareTo(tree.key);
if (cmp < 0) { // 待删除的节点在"tree的左子树"中
tree.left = remove(tree.left, z);
// 删除节点后,若AVL树失去平衡,则进行相应的调节
if (height(tree.right) - height(tree.left) == 2) {
AVLTreeNode<T> r = tree.right;
if (height(r.left) > height(r.right))
tree = rightLeftRotation(tree);
else
tree = rightRightRotation(tree);
}
} else if (cmp > 0) { // 待删除的节点在"tree的右子树"中
tree.right = remove(tree.right, z);
// 删除节点后,若AVL树失去平衡,则进行相应的调节
if (height(tree.left) - height(tree.right) == 2) {
AVLTreeNode<T> l = tree.left;
if (height(l.right) > height(l.left))
tree = leftRightRotation(tree);
else
tree = leftLeftRotation(tree);
}
} else { // tree是对应要删除的节点
// tree的左右孩子都非空
if ((tree.left!=null) && (tree.right!=null)) {
if (height(tree.left) > height(tree.right)) {
// 如果tree的左子树比右子树高;
// 则(01)找出tree的左子树中的最大节点
// (02)将该最大节点的值赋值给tree
// (03)删除该最大节点
// 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的
AVLTreeNode<T> max = maximum(tree.left);
tree.key = max.key;
tree.left = remove(tree.left, max);
} else {
// 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
// 则(01)找出tree的右子树中的最小节点
// (02)将该最小节点的值赋值给tree
// (03)删除该最小节点
// 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的
AVLTreeNode<T> min = minimum(tree.right);
tree.key = min.key;
tree.right = remove(tree.right, min);
}
} else {
tree = (tree.left!=null) ? tree.left : tree.right;
}
}
return tree;
}
/*
* 打印AVL树
*
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
public void print() {
if (mRoot != null)
print(mRoot, mRoot.key, 0);
}
private void print(AVLTreeNode<T> tree, T key, int direction) {
if(tree != null) {
if(direction==0) // tree是根节点
System.out.printf("%d is root\n", tree.key);
else // tree是分支节点
System.out.printf("%d is %d's %s child\n", tree.key, key, direction==1?"right" : "left");
print(tree.left, tree.key, -1);
print(tree.right,tree.key, 1);
}
}
// 主函数
public static void main(String[] args) {
AVLTree<Integer> a = new AVLTree<>();
a.insert(1);a.insert(2);a.insert(3);a.insert(4);a.insert(5);
// a.remove(2);
// a.remove(4);
System.out.println(a.height());
a.print();
}
}
3
2 is root
1 is 2's left child
4 is 2's right child
3 is 4's left child
5 is 4's right child