AVL树
总所周知二叉搜索树会出现一些极端情况,影响查询效率。
为了解决这个问题,就需要把对极端的情况进行调整,优化查询, 比如:
为了实现右图,就需要增加一个旋转功能,我们那它命名为旋转,向右转叫做右旋,向左就叫左旋。
public avlNode leftRate(avlNode node) {
avlNode right = node.right;
node.right = right.left;
right.left = node;
return right;
}
这时候就出现了两个问题
- 什么时候该旋转?
- 如何判定该往哪个方向转?
问题一:什么时候该旋转?
我们可以找到一个最小且极端情况的二叉搜索树作为参考:
下图发生了左旋,b节点成为根,a节点下降成子节点,3层树变成了2层,所以每个节点隐含了高度属性。
h1表示高度1,h3表示高度3,h0表示空节点,高度为0
根据图中得出 a.左.h【a的左子节点高度】 - a.右.h【b的高度】 > 1
int cha = cur.left.height - cur.right.height;
if (Math.abs(cha) <= 1) {
return cur;
}
问题二:如何判定该往哪个方向转?
在即 a.左.h - a.右.h > 1 的基础上,枚举出所有情况如下:
- 情况1需要左旋:判定条件,a.右.右.h - a.右.左.h > 0
- 情况2需要先右旋再左旋:判定条件,a.右.左.h - a.右.右.h > 0
- 情况1需要左旋:判定条件,a.左.右.h - a.左.左.h > 0
- 情况1需要先左旋再右旋:判定条件,a.左.右.h - a.左.左.h > 0
private avlNode<K, V> maintain(avlNode<K, V> cur) {
if (cur == null) {
return null;
}
int leftHeight = cur.left != null ? cur.left.height : 0;
int rightHeight = cur.right != null ? cur.right.height : 0;
if (Math.abs(leftHeight - rightHeight) > 1) {
if (leftHeight > rightHeight) {
int leftLeftHeight = cur.left != null && cur.left.left != null ? cur.left.right.height : 0;
int leftRightHeight = cur.left != null && cur.left.right != null ? cur.left.right.height : 0;
if (leftLeftHeight >= leftRightHeight) {
cur = rightRotate(cur);
} else {
cur.left = leftRotate(cur.left);
cur = rightRotate(cur);
}
} else {
int rightLeftHeight = cur.right != null && cur.right.left != null ? cur.right.left.height : 0;
int rightRightHeight = cur.right != null && cur.right.right != null ? cur.right.right.height : 0;
if (rightRightHeight >= rightLeftHeight) {
cur = leftRotate(cur);
} else {
cur.right = rightRotate(cur.right);
cur = leftRotate(cur);
}
}
}
return cur;
}
问题三:节点增加了高度属性,高度如何维护?
a.h = max(a.左.h, a.右.h) + 1
-
添加节点,父节点高度要重新计算且是递归
public avlNode<K, V> add(avlNode<K, V> cur, K key, V value) { if (cur == null) { return new avlNode<K, V>(key, value); } if (key.compareTo(cur.key) < 0) { cur.left = add(cur.left, key, value); } else { cur.right = add(cur.right, key, value); } // 重新计算高度 cur.height = Math.max(cur.left != null ? cur.left.height : 0, cur.right != null ? cur.right.height : 0) + 1; return maintain(cur); }
-
删除节点,父节点高度也要重新计算,一样是递归
public avlNode<K, V> delete(avlNode<K, V> cur, K key) { if (key.compareTo(cur.key) > 0) { cur.right = delete(cur.right, key); } else if (key.compareTo(cur.key) < 0) { cur.left = delete(cur.left, key); } else { if (cur.left == null && cur.right == null) { // 叶子节点直接删除 cur = null; } else { // 非叶子节点 if (cur.left != null && cur.right != null) { // 双子节点, 找到最接近 当前节点的叶子节点 // 使用该节点替换要删除的节点 avlNode<K, V> des = cur.right; while (des.left != null) { des = des.left; } cur.right = delete(cur.right, des.key); des.left = cur.left; des.left = cur.right; cur = des; } else { // 单子节点,子节点替换当前节点 cur = cur.left != null ? cur.left : cur.right; } } } if (cur != null) { cur.height = Math.max(cur.left != null ? cur.left.height : 0, cur.right != null ? cur.right.height : 0) + 1; } return maintain(cur); }
-
1和2完成后,平衡被破坏的情况下,所以旋转操作的都要重新计算一遍节点高度
private avlNode<K, V> leftRotate(avlNode<K, V> cur) { avlNode<K, V> right = cur.right; cur.right = right.left; right.left = cur; cur.height = Math.max((cur.left != null ? cur.left.height : 0), (cur.right != null ? cur.right.height : 0)) + 1; right.height = Math.max((right.left != null ? right.left.height : 0), (right.right != null ? right.right.height : 0)) + 1; return right; }