AVL树是带有平衡条件的二叉查找树,它允许每个节点的左子树与右子树的高度差未1。这样的平衡树深度是O(log N)。而要维持这种平衡,就必须在每次对AVL树删除节点或新增节点后,检查AVL树的平衡是否被打破,即是否存在节点的左右子树高度差大于1,在判断AVL树失去平衡后,就必须旋转失去平衡的节点,已再次达到平衡。
AVL树旋转:
旋转的目的就是减小树的高度,节点未空时,节点的高度未-1;
1.单旋转:
右旋:
当在节点3的插入节点2前,节点5的高度为1,左节点3的高度为0,插入节点2后,节点5左节点3的高度为1,而右节点高度为-1,两者高度差等于2,节点5失去平衡。 所以已节点3为支点,将节点5右旋至节点3的右节点上。
旋转完成后,AVL树的高度降低,需重新计算每个节点的高度(递归计算)。
左旋:
计算节点高度同上,计算后节点2失去平衡,将节点2旋转到其右节点 3的左节点上,在递归计算AVL树每个节点的高度。
2.双旋转:
先左旋再右旋:
如同所示,在节点3上插入节点4后,节点5失去平衡,如果旋转恢复平衡则要先将节点3旋转至节点4的左节点上,再Jiangxi节点5旋转至节点4的右节点上,通过左旋+右旋两次旋转恢复AVL树的平衡,并重新计算节点高度。
先右旋再左旋:
如图所示,在节点5插入节点4后,AVL树在节点3处失去平衡,因此先将节点5右旋至节点4的右节点上,在讲节点3旋转至节点4的左节点上,通过右旋+左旋两次旋转恢复AVL树的平衡,并重新计算节点高度。
AVL树的添加及删除操作代码如下:
package com.ybin.btree;
/**
* @author yuebing
* @version 1.0
* @Date 2018/5/11
* @category AVL树操作
*/
public class AvlTree<E extends Comparable> {
/**
* 根节点
*/
private Node root;
/**
* AVL树高度
*/
private int height;
private static final class Node<E> {
/**
* 元素值
*/
E element;
/**
* 左节点
*/
Node left;
/**
* 右节点
*/
Node right;
/**
* 节点高度
*/
int nodeHeight;
public Node(E element) {
this(element, null, null);
}
public Node(E element, Node left, Node right) {
this.element = element;
this.left = left;
this.right = right;
}
}
public void add(E e) {
if (e != null)
this.root = insert(e, root);
}
private Node insert (E e, Node node) {
if (node == null) {
return new Node(e);
}
int compare = e.compareTo(node.element);
if (compare < 0) {
node.left = insert(e, node.left);
}else if (compare > 0) {
node.right = insert(e, node.right);
} else {
return node;
}
return balance(node);
}
public void remove(E e) {
if (e != null) {
this.root = this.remove(e, root);
}
}
private Node remove(E e, Node node) {
if (node == null) {
return node;
}
int compare = e.compareTo(node.element);
if (compare < 0) {
node.left = remove(e, node.left);
} else if (compare > 0) {
node.right = remove(e, node.right);
} else if (node.left != null && node.right != null) {
//找出该节点右节点的最小节点,替换被删除的节点
Node min = node.right;
while (min.left != null) {
min = min.left;
}
node.element = min.element;
node.right = remove((E) node.element, node.right);
} else {
node = node.left == null ? node.right : node.left;
}
return this.balance(node);
}
/**
* 添加节点后,平衡AVL树
*
* @param node
* @return
*/
private Node balance(Node node) {
if (node == null) {
return node;
}
if (height(node.left) - height(node.right) > 1) {
if (height(node.left.left) >= height(node.left.right)) {
node = this.rightRotate(node);
} else {
node = this.leftAndRightRotate(node);
}
} else if (height(node.right) - height(node.left) > 1) {
if (height(node.right.right) >= height(node.right.left)) {
node = this.leftRotate(node);
} else {
node = this.rightAndLeftRotate(node);
}
}
node.nodeHeight = Math.max(height(node.left), height(node.right)) + 1;
return node;
}
/**
* 先左旋再右旋
* 1.先将该节点的左孩子节点左旋;
* 2.再将旋转后的节点,右旋;
* 3.重新计算旋转后各节点的高度;
* 4.旋转效果:
* 3 | 3 | 2
* / | / | / \
* 1 -> 2 -> 1 3
* \ | / |
* 2 | 1 |
* @param node
* @return
*/
private Node leftAndRightRotate(Node node) {
node.left = this.leftRotate(node.left);
return rightRotate(node);
}
/**
* 先右旋再左旋
* 1.先将该节点的右孩子节点右旋;
* 2.先将上一步旋转完成的节点,左旋;
* 3.再重新计算旋转后各节点的高度;
* 4.旋转效果:
* 1 | 1 | 2
* \ | \ | / \
* 3 -> 2 -> 1 3
* / | \ |
* 2 | 3 |
* @param node
* @return
*/
private Node rightAndLeftRotate(Node node) {
node.right = this.rightRotate(node.right);
return leftRotate(node);
}
/**
* 左旋
* 1.以传入的节点的右节点为支点,将传入的节点旋转到该支点节点的左节点上;
* 2.重新计算旋转后的节点的高度;
* 3.旋转效果:
* 1 | 2
* \ | / \
* 2 -> 1 3
* \ |
* 3 |
* @param oldNode
* @return
*/
private Node leftRotate(Node oldNode) {
Node newNode = oldNode.right;
oldNode.right = newNode.left;
newNode.left = oldNode;
oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
newNode.nodeHeight = Math.max(height(newNode.right), oldNode.nodeHeight) + 1;
return newNode;
}
/**
* 右旋
* 1.以传入的节点的左节点为支点,将传入的节点旋转到该支点节点的右节点上;
* 2.重新计算旋转后的节点的高度;
* 3.旋转效果:
* 3 | 2
* / | / \
* 2 -> 1 3
* / |
* 1 |
* @param oldNode
* @return
*/
private Node rightRotate(Node oldNode) {
Node newNode = oldNode.left;
oldNode.left = newNode.right;
newNode.right = oldNode;
oldNode.nodeHeight = Math.max(height(oldNode.left), height(oldNode.right)) + 1;
newNode.nodeHeight = Math.max(height(newNode.left), oldNode.nodeHeight) + 1;
return newNode;
}
/**
* 获取节点高度
* 1.节点为空,则高度为-1;
* 2.否则返回节点高度;
*
* @param node
* @return
*/
private int height(Node node) {
return node == null ? -1 : node.nodeHeight;
}
}