满二叉数:除了叶子节点,其余节点都有左右子树
平衡二叉树:
- 任意一个节点的左子树和右子树的高度差不超过1
- 平衡二叉树的高度和节点数量之间的关系也是O(log n)
一、计算节点的高度和平衡因子
- 节点加一个
height
值,标识节点的高度 - 计算平衡因子(左 - 右 高度差,平衡二叉树要满足这个绝对值不能大于1)
二、检查二分搜索树性质和平衡性
- 1、二分搜索树:每个节点的左子树的值都小于该节点的值,每个节点右子树的值都大于该节点的值
中序遍历放到一个ArrayList
中, 升序表明是一颗二分搜索树 - 2、判断以Node为根的二叉树是否是一棵平衡二叉树
三、旋转操作的基本原理
加入节点后,打破了平衡性,导致新增节点的父祖辈节点深度/平衡因子可能发生变化,于是可以沿着节点向上维护平衡性,可以使用递归实现。 - 旋转并不影响二分搜索树满足条件
// 平衡维护
// LL
if (balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
return rightRotate(node);
// RR
if (balanceFactor < -1 && getBalanceFactor(node.right) <= 0)
return leftRotate(node);
// LR
if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
// RL
if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
1) LL——右旋转(插入z元素在不平衡节点的左侧的左侧)
// 对节点y进行向右旋转操作,返回旋转后新的根节点x
// y x
// / \ / \
// x T4 向右旋转 (y) z y
// / \ - - - - - - - -> / \ / \
// z T3 T1 T2 T3 T4
// / \
// T1 T2
private Node rightRotate(Node y) {
Node x = y.left;
Node T3 = x.right;
// 向右旋转过程
x.right = y;
y.left = T3;
// 更新height
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
2) RR——左旋转(插入z元素在不平衡节点的右侧的右侧)
// 对节点y进行向左旋转操作,返回旋转后新的根节点x
// y x
// / \ / \
// T1 x 向左旋转 (y) y z
// / \ - - - - - - - -> / \ / \
// T2 z T1 T2 T3 T4
// / \
// T3 T4
private Node leftRotate(Node y) {
Node x = y.right;
Node T2 = x.left;
// 向左旋转过程
x.left = y;
y.right = T2;
// 更新height
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x;
}
3) LR——(插入的元素在不平衡节点的左侧的右侧)
我们看下上图的左边,如果我们现在使用右旋转,则12变成8的左孩子,10变成8的右孩子,违反了二分搜索树的原则。所以我们将上面的情况抽象:
此时,我们先对x进行左旋转
可以看到,此时已经转换成LL的情况了,然后我们再按第一节右旋转的方法对y节点进行右旋转就可以啦
// LR
if (balanceFactor > 1 && getBalanceFactor(node.left) < 0) {
node.left = leftRotate(node.left);
return rightRotate(node);
}
4) RL——(插入的元素在不平衡节点的右侧的左侧)
然后再对y进行左旋转就可以了
// RL
if (balanceFactor < -1 && getBalanceFactor(node.right) > 0) {
node.right = rightRotate(node.right);
return leftRotate(node);
}
四、从AVL树中删除元素
private Node remove(Node node, K key){
if( node == null )
return null;
Node retNode;
if( key.compareTo(node.key) < 0 ){
node.left = remove(node.left , key);
// return node;
retNode = node;
} else if(key.compareTo(node.key) > 0 ){
node.right = remove(node.right, key);
// return node;
retNode = node;
} else { // key.compareTo(node.key) == 0
// 待删除节点左子树为空的情况
if(node.left == null){
Node rightNode = node.right;
node.right = null;
size --;
// return rightNode;
retNode = rightNode;
} else if(node.right == null){ // 待删除节点右子树为空的情况
Node leftNode = node.left;
node.left = null;
size --;
// return leftNode;
retNode = leftNode;
} else { // 待删除节点左右子树均不为空的情况
// 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点
// 用这个节点顶替待删除节点的位置
Node successor = minimum(node.right);
// successor.right = removeMin(node.right);
successor.right = remove(node.right, successor.key);
successor.left = node.left;
node.left = node.right = null;
// return successor;
retNode = successor;
}
}
if(retNode == null)
return null;
// 更新height
retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
// 计算平衡因子
int balanceFactor = getBalanceFactor(retNode);
// 平衡维护
// LL
if (balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
return rightRotate(retNode);
// RR
if (balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0)
return leftRotate(retNode);
// LR
if (balanceFactor > 1 && getBalanceFactor(retNode.left) < 0) {
retNode.left = leftRotate(retNode.left);
return rightRotate(retNode);
}
// RL
if (balanceFactor < -1 && getBalanceFactor(retNode.right) > 0) {
retNode.right = rightRotate(retNode.right);
return leftRotate(retNode);
}
return retNode;
}