1.AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家发明了一种解决上述问题的方法:
当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过
1 (需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
如何识别一颗二叉树是AVL树?
- 它的左右子树都是AVL树
- 左右子树高度之差的绝对值不超过1
如果一颗二叉树是高度平衡的,它就是AVL树,如果它有n个节点,其高度可以保持在O(log2^n),搜索的时间复杂度O(log2^n)。
2.AVL树的优缺点:
优点:
查找效率高,查找的时间复杂度O(log2^N)
缺点:
插入:AVL必须要保证严格平衡,插入一次数据就可能需要(左单旋,右单旋,左右双旋,右左双旋)
删除:和二叉搜索树删除一样,然后再更新平衡因子,出现不平衡,在进行旋转
所以说,AVL树的插入,删除代价大
AVL树有缺点,所以才构造了红黑树
AVL树适用场景:固定不变的文件查找
3.AVL树的实现
package test;
class TreeNode {
int val;
int bf;
TreeNode left;
TreeNode right;
TreeNode parent;
public TreeNode(int val) {
this.val = val;
}
}
public class AVLTree {
public TreeNode root;
public boolean insert(int val) {
TreeNode node = new TreeNode(val);
if (root == null) {
root = node;
return true;
}
TreeNode pre = null;
TreeNode cur = root;
while (cur != null) {
if (cur.val < val) {
pre = cur;
cur = cur.right;
} else if (cur.val > val) {
pre = cur;
cur = cur.left;
} else {
return false;
}
}
if (pre.val < val) {
pre.right = node;
} else {
pre.left = node;
}
//插入新元素后根据平衡因子进行对树的调整
node.parent = pre;
cur = node;
//调节平衡因子
while (pre != null) {
//先看cur是parent的左还是右,决定平衡因子++还是--
if (cur == pre.right) {
//如果是右树,那么右树高度增加,平衡因子++
pre.bf++;
} else {
//如果是左树,那么右树高度增加,平衡因子--
pre.bf--;
}
//检查当前的平衡因子 是不是绝对值 1 0 -1
if (pre.bf == 0) {
//刚插入就平衡,不用继续向上调整
break;
} else if (pre.bf == 1 || pre.bf == -1) {
//插入之后打破平衡,继续向上调整
cur = pre;
pre = cur.parent;
} else {
if (pre.bf == 2) {//右树高,左旋
if (cur.bf == 1) {
//左单旋
rotateLeft(pre);
} else {
//cur.bf == -1
rotateRL(pre);
}
} else {
//pre.bf == -2 左树高,需要降低左树的高度
if (cur.bf == -1) {
//右单旋
rotateRight(pre);
} else {
//cur.bf == 1
//左右双旋
rotateLR(pre);
}
}
break;
}
}
return true;
}
//右左双旋
public void rotateRL(TreeNode pre) {
TreeNode subR = pre.right;
TreeNode subRL = subR.left;
int bf = subRL.bf;
rotateRight(pre.right);
rotateLeft(pre);
if (bf == 1) {
pre.bf = -1;
subR.bf = 0;
subRL.bf = 0;
} else if (bf == -1) {
pre.bf = 0;
subR.bf = 1;
subRL.bf = 0;
}
}
//左右双旋
public void rotateLR(TreeNode pre) {
TreeNode subL = pre.left;
TreeNode subLR = subL.right;
int bf = subLR.bf;
rotateLeft(pre.left);
rotateRight(pre);
if (bf == -1) {
subL.bf = 0;
subLR.bf = 0;
pre.bf = 1;
} else if (bf == 1) {
subL.bf = -1;
subLR.bf = 0;
pre.bf = 0;
}
}
//左单旋
public void rotateLeft(TreeNode pre) {
TreeNode subR = pre.right;
TreeNode subRL = subR.left;
pre.right = subRL;
subR.left = pre;
if (subRL != null) {
subRL.parent = pre;
}
TreeNode pPre = pre.parent;
pre.parent = subR;
if (root == pre) {
root = subR;
root.parent = null;
} else {
if (pPre.left == pre) {
pPre.left = subR;
} else {
pPre.right = subR;
}
subR.parent = pPre;
}
subR.bf = 0;
pre.bf = 0;
}
//右单旋
public void rotateRight(TreeNode pre) {
TreeNode subL = pre.left;
TreeNode subLR = subL.right;
pre.left = subLR;
subL.right = pre;
//没有subLR的时候
if (subLR != null) {
subLR.parent = pre;
}
TreeNode pPre = pre.parent;
pre.parent = subL;
//检查当前是不是根节点
if (pre == root) {
root = subL;
root.parent = null;
} else {
//不是根节点,判断这棵树是左子树还是右子树
if (pPre.left == pre) {
pPre.left = subL;
} else {
pPre.right = subL;
}
subL.parent = pPre;
}
subL.bf = 0;
pre.bf = 0;
}
//验证是不是AVL树
public boolean isBalance(TreeNode root) {
if (root == null) {
return true;
}
int leftH = height(root.left);
int rightH = height(root.right);
if (rightH - leftH != root.bf) {
System.out.println(root.val + " 平衡因子异常");
return false;
}
return Math.abs(leftH - rightH) <= 1 && isBalance(root.left) && isBalance(root.right);
}
//中序遍历
public void inorder(TreeNode root) {
if (root == null) {
return;
}
inorder(root.left);
System.out.println(" " + root.val + " ");
inorder(root.right);
}
public int height(TreeNode root) {
if (root == null) {
return 0;
}
int leftH = height(root.left);
int rightH = height(root.right);
return leftH > rightH ? leftH + 1 : rightH + 1;
}
public static void main(String[] args) {
// int[] array1 = {16, 3, 7, 11, 9, 26, 18, 14, 15};
int[] array2 = {4, 2, 6, 1, 3, 5, 15, 7, 16, 14};
AVLTree avlTree = new AVLTree();
// for (int i = 0; i < array1.length; i++) {
// avlTree.insert(array1[i]);
// }
for (int i = 0; i < array2.length; i++) {
avlTree.insert(array2[i]);
}
System.out.println(avlTree.isBalance(avlTree.root));
}
}
未完待续~