AVL
目录
概述
二叉搜索树(BST)可能会造成如下图所示的问题:
查找的时间复杂度会达到O(N),这个时候AVL树就派上用场了。
一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉搜索树(空树的高度定义为-1)。
把失去平衡的节点叫做α,由于任意节点最多有两个孩子,因此出现高度不平衡的α的两棵子树的高度差为2。容易看出,这种不平衡可能会出现以下四种情况:
- 对α的左孩子的左子树进行一次插入
- 对α的左孩子的右子树进行一次插入
- 对α的右孩子的左子树进行一次插入
- 对α的右孩子的右子树进行一次插入
情形1和4是对称的,2和3也是对称的。因此,理论上只有两种情形。
单旋转
右旋转
情形1如下图所示:
在k2的左孩子k1的左子树插入一个元素,导致k2的左右子树不平衡了(插入前是平衡的),此时以k1为轴,进行一次右旋转(顺时针)即可达到平衡。
左旋转
同理,就很容易得出对称的情形4:
在k1的右孩子k2的右子树插入一个元素,导致k1的左右子树不平衡(插入前是平衡的),此时以k2为轴,进行一次左旋转(逆时针)即可达到平衡。
双旋转
左-右旋转
情形2如图所示:
假设在k3的左孩子k1的右子树上k2插入一个元素,导致B或者C比D深两层,那么此时只需要:
- 以k1为轴,先做一次左旋转(情形1)
- 再次k3为轴,做一次右旋转(情形4)
即可让整棵树再次达到平衡,也就是两次单旋转即可。
右-左旋转
同样,对于情形3,如图:
在k1的右孩子k3的左子树k2上插入一个元素,导致B或者C比A深两层,那么此时只需要:
- 以k3为轴,先做一次右旋转(情形4)
- 再以k1为轴,做一次左旋转(情形1)
即可让整棵树再次达到平衡,也是两次单旋转。
代码实现
已经明白了旋转的原理,剩下的就是编码实现了,弄清楚指针指向就行了。
public class AVLTreeTest {
public static void main(String[] agrs) {
AVLTree tree = null;
for(Integer i = 1; i < 10; i++) {
tree = AVLTreeUtils.insert(i, tree);
}
System.out.println("preOrder: ");
AVLTreeUtils.preOrder(tree);
System.out.println();
System.out.println("inOrder: ");
AVLTreeUtils.inOrder(tree);
System.out.println();
System.out.println("postOrder: ");
AVLTreeUtils.postOrder(tree);
}
}
class AVLTree {
public Integer element;
public AVLTree lchild;
public AVLTree rchild;
public Integer height;
public AVLTree() {}
}
class AVLTreeUtils {
public static AVLTree insert(Integer ele, AVLTree root) {
if(root == null) {// 如果是空节点,就插入
root = new AVLTree();
root.element = ele;
root.lchild = root.rchild = null;
root.height = 0;
} else if(ele < root.element) {// 在左子树插入
root.lchild = insert(ele, root.lchild);
// 插入后,检查一下是否已经失衡
if(getHeight(root.lchild) - getHeight(root.rchild) == 2) {
if(ele < root.lchild.element)
// 左孩子的左子树——情形1
root = singleRotateWithLeft(root);
else
// 左孩子的右子树--情形2
root = doubleRotateWithLeft(root);
}
} else if(ele > root.element) {
root.rchild = insert(ele, root.rchild);
if(getHeight(root.rchild) - getHeight(root.lchild) == 2) {
if(ele > root.rchild.element)
// 右孩子的左子树--情形3
root = singleRotateWithRight(root);
else
// 右孩子的右子树--情形4
root = doubleRotateWithRight(root);
}
}
// 调整完成后,更新一下整棵树的高度
root.height = Math.max(getHeight(root.lchild), getHeight(root.rchild)) + 1;
return root;
}
private static Integer getHeight(AVLTree tree) {
if (tree == null)
return -1;
return tree.height;
}
// 对照几张示意图,即可弄明白指针的指向
// 情形1
private static AVLTree singleRotateWithLeft(AVLTree oldRoot) {
AVLTree newRoot = oldRoot.lchild;;
oldRoot.lchild = newRoot.rchild;
newRoot.rchild = oldRoot;
oldRoot.height = Math.max(getHeight(oldRoot.lchild), getHeight(oldRoot.rchild)) + 1;
newRoot.height = Math.max(getHeight(newRoot.lchild), oldRoot.height) + 1;
return newRoot;
};
// 情形4
private static AVLTree singleRotateWithRight(AVLTree oldRoot) {
AVLTree newRoot = oldRoot.rchild;;
oldRoot.rchild = newRoot.lchild;
newRoot.lchild = oldRoot;
oldRoot.height = Math.max(getHeight(oldRoot.lchild), getHeight(oldRoot.rchild)) + 1;
newRoot.height = Math.max(oldRoot.height, getHeight(newRoot.rchild)) + 1;
return newRoot;
};
// 情形2
private static AVLTree doubleRotateWithLeft(AVLTree oldRoot) {
oldRoot.lchild = singleRotateWithRight(oldRoot.lchild);
return singleRotateWithLeft(oldRoot);
};
// 情形3
private static AVLTree doubleRotateWithRight(AVLTree oldRoot) {
oldRoot.rchild = singleRotateWithLeft(oldRoot.rchild);
return singleRotateWithRight(oldRoot);
};
// 先序遍历
public static void preOrder(AVLTree tree) {
if(tree != null) {
System.out.print(tree.element + " ");
preOrder(tree.lchild);
preOrder(tree.rchild);
}
}
// 中序遍历
public static void inOrder(AVLTree tree) {
if(tree != null) {
inOrder(tree.lchild);
System.out.print(tree.element + " ");
inOrder(tree.rchild);
}
}
// 后序遍历
public static void postOrder(AVLTree tree) {
if(tree != null) {
postOrder(tree.lchild);
postOrder(tree.rchild);
System.out.print(tree.element + " ");
}
}
}
// output
preOrder:
4 2 1 3 6 5 8 7 9
inOrder:
1 2 3 4 5 6 7 8 9
postOrder:
1 3 2 5 7 9 8 6 4
Summary
以前挺畏惧树的,可是仔细研究一下 – Just a pretty girl standing out there in the fog waiting to be saved…