知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!
AVL树的定义
BST树最差的情况下,就退化成一条链表了(比如一组有序的元素插入BST树),增删查的时间复杂度就无法达到 O ( l o g 2 n ) O(log_2n) O(log2n),AVL树在BST数的基础上,加入了节点平衡的概念,平衡指的是任意一个节点的左子树和右子树的高度差不能超过1,那么这颗树就是一颗平衡树,否则就要通过既定规则的旋转操作,使节点重新达到平衡状态。
AVL树就是平衡二叉树,AVL树中每一个节点的左右子树高度差不会超过1,下面提供AVL树类型的基本定义:
/**
* AVL树节点类型
* @param <T>
*/
class AVLNode<T extends Comparable<T>>{
private T data;
private int height; // 用来记录以当前节点为根节点的树的高度
private AVLNode<T> left;
private AVLNode<T> right;
public AVLNode(T data, int height) {
this.data = data;
this.height = height;
this.left = this.right = null;
}
public AVLNode(T data, int height, AVLNode<T> left, AVLNode<T> right) {
this.data = data;
this.height = height;
this.left = left;
this.right = right;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public AVLNode<T> getLeft() {
return left;
}
public void setLeft(AVLNode<T> left) {
this.left = left;
}
public AVLNode<T> getRight() {
return right;
}
public void setRight(AVLNode<T> right) {
this.right = right;
}
}
/**
AVL树的类定义
*/
class AVL<T extends Comparable<T>>{
private AVLNode<T> root;
/**
* 获取节点node的左右子树高度差
* @param node
* @return
*/
public int height(AVLNode<T> node){
return node == null ? 0 : node.getHeight();
}
/**
* 返回当前节点左右子树的高度最高值
* @param left
* @param right
* @return
*/
public int maxHeight(AVLNode<T> left, AVLNode<T> right){
return height(left) > height(right) ? height(left) : height(right);
}
}
AVL树的四种旋转操作
AVL树为了维护节点平衡,总共包含四种旋转操作,分别是:
1.节点失衡,是由于左孩子的左子树太高造成的,直接进行右旋转操作,代码如下:
/**
* AVL树的右旋操作,以形参node为根节点进行右旋转,旋转完成以后,把新的根节点返回,
* 并更新相关节点的高度值(从下往上更新,因为父节点的高度值要依赖子节点进行计算)
* 40 30
* 30 => 20 40
* 20
* @param node
* @return
*/
private AVLNode<T> rotateRight(AVLNode<T> node){
AVLNode<T> child = node.getLeft();
node.setLeft(child.getRight());
child.setRight(node);
node.setHeight(maxHeight(node.getLeft(), node.getRight()) + 1);
child.setHeight(maxHeight(child.getLeft(), child.getRight()) + 1);
return child;
}
2.节点失衡,是由于右孩子的右子树太高造成的,进行左旋转操作,代码如下:
/**
* AVL树的左旋转操作,以形参node为根节点进行左旋转,旋转完成以后,把新的根节点返回,
* 并更新相关节点的高度值(从下往上更新,因为父节点的高度值要依赖子节点进行计算)
* 40 50
* 50 => 40 60
* 60
* @param node
* @return
*/
private AVLNode<T> rotateLeft(AVLNode<T> node){
AVLNode<T> child = node.getRight();
node.setRight(child.getLeft());
child.setLeft(node);
node.setHeight(maxHeight(node.getLeft(), node.getRight()) + 1);
child.setHeight(maxHeight(child.getLeft(), child.getRight()) + 1);
return child;
}
3.节点失衡,是由于左孩子的右子树太高造成的,进行左-右旋转(也叫左平衡操作)操作,代码如下:
/**
* AVL树的左平衡 - 左右旋转,先以node的左孩子为根节点进行左旋转操作,再以node为根节点进行右旋
* 转操作
* 40 40 30
* 30 => 30 => 20 40
* 20 20
* @param node
* @return
*/
private AVLNode<T> leftBalance(AVLNode<T> node){
node.setLeft(rotateLeft(node.getLeft()));
return rotateRight(node);
}
4.节点失衡,是由于右孩子的左子树太高造成的,进行右-左旋转(也叫右平衡操作)操作,代码如下:
/**
* AVL树的右平衡 - 右左平衡,先以node的右孩子为根节点进行右旋转操作,再以node为根节点进行左旋
* 转操作
* 40 40 50
* 60 => 50 => 40 60
* 50 60
* @param node
* @return
*/
private AVLNode<T> rightBalance(AVLNode<T> node){
node.setRight(rotateRight(node.getRight()));
return rotateLeft(node);
}
AVL树的插入操作
AVL树的插入操作,是在BST树的插入操作上引入相应的旋转操作,代码如下:
/**
* AVL树插入数据
* @param val
*/
public void insert(T val){
this.root = insert(this.root, val);
}
private AVLNode<T> insert(AVLNode<T> root, T val) {
if(root == null){
return new AVLNode<>(val, 1);
}
if(root.getData().compareTo(val) > 0){
root.setLeft(insert(root.getLeft(), val));
// 插入左子树 判断当前节点root是否失衡
if(height(root.getLeft()) - height(root.getRight()) > 1){
if(root.getLeft().getData().compareTo(val) > 0){
// 左孩子的左子树失衡 右旋转操作
root = rotateRight(root);
} else {
// 左孩子的右子树失衡 左右旋转
root = leftBalance(root);
}
}
} else if(root.getData().compareTo(val) < 0){
root.setRight(insert(root.getRight(), val));
// 插入右子树 判断当前节点root是否失衡
if(height(root.getRight()) - height(root.getLeft()) > 1){
if(root.getRight().getData().compareTo(val) < 0){
// 右孩子的右子树失衡 左旋转操作
root = rotateLeft(root);
} else {
// 右孩子的左子树失衡 右左旋转
root = rightBalance(root);
}
}
} else {
;
}
// 更新节点高度
root.setHeight(maxHeight(root.getLeft(), root.getRight()) + 1);
return root;
}
AVL树的删除操作
AVL树的删除操作,是在BST树的删除操作上引入相应的旋转操作,代码如下:
/**
* 删除AVL数的节点
* @param val
*/
public void remove(T val){
this.root = remove(this.root, val);
}
private AVLNode<T> remove(AVLNode<T> root, T val) {
if(root == null){
return null;
}
if(root.getData().compareTo(val) > 0){
root.setLeft(remove(root.getLeft(), val));
// 判断当前节点root是否失衡
if(height(root.getRight()) - height(root.getLeft()) > 1){
if(height(root.getRight().getRight())
>= height(root.getRight().getLeft())){
root = rotateLeft(root); // 右孩子的右子树太高,左旋转操作
} else {
root = rightBalance(root); // 右孩子的左子树太高,右平衡操作
}
}
} else if(root.getData().compareTo(val) < 0){
root.setRight(remove(root.getRight(), val));
// 判断当前节点root是否失衡
if(height(root.getLeft()) - height(root.getRight()) > 1){
if(height(root.getLeft().getLeft())
>= height(root.getLeft().getRight())){
root = rotateRight(root); // 左孩子的左子树太高,右旋转操作
} else {
root = leftBalance(root); // 左孩子的右子树太高,左平衡操作
}
}
} else {
if(root.getLeft() != null && root.getRight() != null){
// 比较左子树和右子树的高度,谁高删除谁,不用旋转
if(height(root.getLeft()) > height(root.getRight())){
// 删除前驱
AVLNode<T> pre = root.getLeft();
while(pre.getRight() != null){
pre = pre.getRight();
}
root.setData(pre.getData());
root.setLeft(remove(root.getLeft(), pre.getData()));
} else {
// 删除后继
AVLNode<T> last = root.getRight();
while(last.getLeft() != null){
last = last.getLeft();
}
root.setData(last.getData());
root.setRight(remove(root.getRight(), last.getData()));
}
} else if(root.getLeft() != null){
return root.getLeft();
} else if(root.getRight() != null){
return root.getRight();
} else {
return null;
}
}
// 更新节点高度
root.setHeight(maxHeight(root.getLeft(), root.getRight()) + 1);
return root;
}
AVL树的其它代码操作和BST树一样,可参考我写的BST树介绍的文章代码。