1、定义
平衡搜索二叉树,相对于搜索二叉树而言,AVL树又多了一个性质:左右子树的高度差不大于1.
2、平衡因子,balance factor,以下简称bf,是左子树高度减去右子树的高度
- bf > 1,左边子树高
- bf < -1,右边子树高
- bf = 0, -1, 1 是平衡的
// 节点的高度 private int height(AVLNode node) { return node == null ? 0 : node.height; } // 更新一个节点的高度,用于新增,删除,旋转 private void updateHeight(AVLNode node) { node.height = Math.max(height(node.right), height(node.left)) + 1; }
3、失衡的四种情况:
- LL :失衡节点左子树高,并且失衡节点的左孩子的左子树高----- 一次右旋
- LR:失衡节点左子树高,并且失衡节点的左孩子的右子树高 ---- 左子树先左旋,失衡节点右旋
- RL:失衡节点的右子树高,并且失衡节点的右孩子的左子树高 ---- 右子树先右旋,失衡节点在左旋
- RR:失衡节点的右子树高,并且失衡节点的右孩子的右子树高 ---- 一次左旋
4、旋转操作
右旋方法:传入要旋转的节点,返回值是新的根节点
public AVLNode rightRotate(AVLNode red) { AVLNode yellow = red.left; AVLNode green = yellow.right; yellow.right = red; red.left = green; // 更新节点高度,其实只要更新红色和黄色节点高度就可以了 updateHeight(red); updateHeight(yellow); return yellow; }
左旋方法
public AVLNode leftRotate(AVLNode red) { AVLNode yellow = red.right; AVLNode green = yellow.left; yellow.left = red; red.right = green; // 更新节点高度,其实只要更新红色和黄色节点高度就可以了 updateHeight(red); updateHeight(yellow); return yellow; }
左右旋方法 --- 先左旋左子树,在右旋跟节点
- 先对做子树左旋,左旋完之后返回的节点,作为该节点的左子树
- 然后对这个树进行右旋
public AVLNode leftRightRotate(AVLNode node) { node.left = leftRotate(node.left); return rightRotate(node); }
右左旋方法:
- 先对右子树右旋,右旋完之后的节点作为该节点的右子树
- 然后对该节点进行左旋
public AVLNode rightLeftRotate(AVLNode node) { node.right = rightRotate(node.right); return leftRotate(node); }
5、平衡操作,发生在新增,删除时,对树进行平衡的操作
检查节点是否失衡,如果失衡,就平衡,然后返回新的跟节点
// 检查节点是否失衡,如果失衡,就平衡,然后返回新的跟节点 public AVLNode balance(AVLNode node) { if (node == null) { return null; } int bf = bf(node); if (bf == 0 || bf == 1 || bf == -1) { return node; } if (bf > 1) { int leftBf = bf(node.left); if (leftBf >= 0) { // --注意等号,发生在删除时 // LL return rightRotate(node); } else { // LR return leftRightRotate(node); } } else { int rightBf = bf(node.right); if (rightBf <= 0) { // --注意等号,发生在删除时 // RR return leftRotate(node); } else { // RL return rightLeftRotate(node); } } }
6、节点的插入操作
插入的时候,要更新节点高度,并平衡树
public AVLNode root; // 插入一个节点 public void put(int key, Object value) { root = doPut(root, key, value); } // 递归调用 public AVLNode doPut(AVLNode node, int key, Object value) { // 节点空,就返回一个新节点, if (node == null) { return new AVLNode(key, value); } if (node.key == key) { node.value = value; return node; } if (node.key > key) { node.left = doPut(node.left, key, value); // 向左 } else { node.right = doPut(node.right, key, value); // 向右 } // 更新节点的高度,并对树进行平衡操作 updateHeight(node); return balance(node); }
7、 节点的删除操作
public void remove(int key) {
root = doRemove(root, key);
}
public AVLNode doRemove(AVLNode node, int key) {
if (node == null) {
return null;
}
if (key < node.key) {
node.left = doRemove(node.left, key);
} else if (node.key < key) {
node.right = doRemove(node.right, key);
} else {
// 找到了要删除的节点
if (node.left == null) {
node = node.right;
} else if (node.right == null) {
node = node.left;
} else {
// 找后继节点
AVLNode s = node.right;
while (s.left != null) {
s = s.left;
}
s.right = doRemove(s.right, s.key);
s.left = node.left;
node = s;
}
}
updateHeight(node);
return balance(node);
}