定义:
通常高度平衡树用符号HB(k)表示,其中k为左子树和右子树的高度差,k叫做平衡因子
在HB(k)中,如果k = 1,那么这样的二叉搜索树叫做AVL树,即一棵AVL树是带有平衡条件的二叉搜索树;左子树和右子树的高度差最多不能超过1。
1.AVL树的性质
一棵二叉树为AVL树,当且仅当满足如下条件:
- 它是一棵二叉搜索树
- 对任意结点X,其左子树的高度与其右子树的高度的差最多不超过1。
2.AVL树的作用
对于一般的二叉搜索树,其期望高度(即为一棵平衡树时)为log2n,其各操作的时间复杂度O(log2n)同时也由此而决定。但是,在某些极端的情况下(如在插入的序列是有序的时),二叉搜索树将退化成近似链或链,此时,其操作的时间复杂度将退化成线性的,即O(n)。我们可以通过随机化建立二叉搜索树来尽量的避免这种情况,但是在进行了多次的操作之后,由于在删除时,我们总是选择将待删除节点的后继代替它本身,这样就会造成总是右边的节点数目减少,以至于树向左偏沉。这同时也会造成树的平衡性受到破坏,提高它的操作的时间复杂度。
3. 旋转
如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图:
上图中的4棵树都是"失去平衡的AVL树",从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:
上面的两张图都是为了便于理解,而列举的关于"失去平衡的AVL树"的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都有各自的定义:
(1)LL:LeftLeft,也称为"左左"。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
例如,在上面LL情况中,由于"根节点(8)的左子树(4)的左子树(2)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)“高2。
(2)LR:LeftRight,也称为"左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致"根的左子树的高度"比"根的右子树的高度"大2,导致AVL树失去了平衡。
例如,在上面LR情况中,由于"根节点(8)的左子树(4)的左子树(6)还有非空子节点",而"根节点(8)的右子树(12)没有子节点";导致"根节点(8)的左子树(4)高度"比"根节点(8)的右子树(12)“高2。
(3)RL:RightLeft,称为"右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。
例如,在上面RL情况中,由于"根节点(8)的右子树(12)的左子树(10)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)“高2。
(4) RR:RightRight,称为"右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致"根的右子树的高度"比"根的左子树的高度"大2,导致AVL树失去了平衡。
例如,在上面RR情况中,由于"根节点(8)的右子树(12)的右子树(14)还有非空子节点",而"根节点(8)的左子树(4)没有子节点";导致"根节点(8)的右子树(12)高度"比"根节点(8)的左子树(4)"高2。
如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。AVL失去平衡之后,可以通过旋转使其恢复平衡,下面分别介绍"LL(左左),LR(左右),RR(右右)和RL(右左)"这4种情况对应的旋转方法。
4.Java版本实现
package tree;
public class AVLTree<T> {
class Node<T>{
private Node<T> left = null;
private Node<T> right = null;
private T data;
private int hight;
public Node(T d) {
data = d;
hight = 1;
}
public Node<T> getLeft() {
return left;
}
public void setLeft(Node<T> left) {
this.left = left;
}
public Node<T> getRight() {
return right;
}
public void setRight(Node<T> right) {
this.right = right;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getHight() {
return hight;
}
public void setHight(int hight) {
this.hight = hight;
}
}
private Node<T> root;
public AVLTree(T value) {
root = new Node<T>(value);
}
//RR型旋转
Node<T> RRRoation(Node<T> k1) {
Node<T> k2;
k2 = k1.getRight();
k1.setRight(k2.getLeft());
k2.setLeft(k1);
return k2;
}
//LL型旋转
Node<T> LLRoation(Node<T> k2){
Node<T> k1;
k1 = k2.getLeft();
k2.setLeft(k1.getRight());
k1.setRight(k2);
return k1;
}
//RL型旋转
Node<T> RLRoation(Node<T> k3){
k3.setRight(LLRoation(k3.getRight()));
return RRRoation(k3);
}
//LR型旋转
Node<T> LRRoation(Node<T> k4){
k4.setLeft(RRRoation(k4.getLeft()));
return LLRoation(k4);
}
//获取树的深度
int getDepth(Node<T> root , int initDeep){
if(root == null)
return initDeep;
int leftDeep = initDeep;
int rightDeep = initDeep;
if(root.getLeft() != null)
leftDeep = getDepth(root.getLeft() , initDeep++);
if(root.getRight() != null)
rightDeep = getDepth(root.getRight(), initDeep++);
return leftDeep > rightDeep ? leftDeep : rightDeep;
}
//判断树是否失去平衡
boolean unBanlace(Node<T> root) {
if(root == null)
return false;
int leftHeight = getDepth(root.getLeft(), 1);
int rightHeight = getDepth(root.getRight(), 1);
return Math.abs(leftHeight - rightHeight) > 1;
}
//插入操作的入口
public void insert(T value) {
insert(root, value);
}
//插入操作的递归实现
private Node<T> insert(Node<T> root , T value) {
if(root == null) {
root = new Node<T>(value);
return root;
}
if((int)value < (int)root.getData()) {//插入左子树
root.setLeft(insert(root.getLeft() , value));
if(unBanlace(root)) {
if((int)value < (int)root.getLeft().getData()) {
//LL型旋转
root = LLRoation(root);
}else {
//LR型旋转
root = LRRoation(root);
}
}
}else if((int)value > (int)root.getData()){//插入右子树
root.setRight(insert(root.getRight() , value));
if(unBanlace(root)) {
if((int)value > (int)root.getRight().getData()) {
//RR型旋转
root = RRRoation(root);
}else {
//RL型旋转
root = RLRoation(root);
}
}
}else {
throw new RuntimeException("duplicate value: " + value);
}
return root;
}
//删除结点的入口
public Node<T> remove(T value){
return remove(root , value);
}
//删除结点的递归实现
private Node<T> remove(Node<T> root , T value){
if(root == null)
return null;
if((int)value < (int)root.getData() ) {
//欲删除结点在左子树
root.setLeft(remove(root.getLeft() , value));
}else if((int)value > (int)root.getData() ) {
//欲删除结点在右子树
root.setRight(remove(root.getRight() , value));
}else if(root.getData() == value){
//欲删除结点是根结点本身
//左子树和右子树同时存在
if(root.getLeft() != null && root.getRight() != null) {
//左子树比右子树高
if(getDepth(root.getLeft(), 1) > getDepth(root.getRight(), 1)) {
Node<T> max = getMax(root.getLeft());
root.setData(max.getData());
root.setLeft(remove(root.getLeft(),max.getData()));
}else {
//左子树不高于右子树
Node<T> min = getMin(root.getRight());
root.setData(min.getData());
root.setRight(remove(root.getRight(),min.getData()));
}
}else {
//左子树和右子树不同时存在
root = root.getLeft() == null ? root.getRight() : root.getLeft();
}
}else {
System.out.println("no node matched value: " + value);
}
return root;
}
//获取最大结点
Node<T> getMax(Node<T> root){
if(root == null)
return null;
if(root.getRight() != null) {
return getMax(root.getRight());
}else {
return root;
}
}
//获取最小结点
Node<T> getMin(Node<T> root){
if(root == null)
return null;
if(root.getLeft() != null) {
return getMin(root.getLeft());
}else {
return root;
}
}
//先序遍历
private void inOrder(Node<T> root) {
if(root != null) {
inOrder(root.getLeft());
System.out.print(root.getData() + "->");
inOrder(root.getRight());
}
}
public static void main(String[] args) {
AVLTree<Integer> avlTree = new AVLTree<>(3);
int arr[]= {2,1,4,5,6,7,16,15,14,13,12,11,10,8,9};
for (int a : arr) {
avlTree.insert(a);
}
avlTree.inOrder(avlTree.root);
avlTree.remove(3);
System.out.println();
avlTree.inOrder(avlTree.root);
}
}