AVL树(平衡二叉树)

一、AVL树的特征

  虽说二叉查找树是一种优秀的数据结构,能够大大降低数据查询的复杂度。但是,并不是说有情况下二叉树都能够达到快速查找的目的。
这里写图片描述
  我们发现,如果按照[7,10,11,12,14,15,18]这样的顺序一个个元素进行插入的话就会出现右图所示的二叉树,这样的二叉树跟一个链表几乎是没有区别的,查找的效率一样,没有体现出二叉树的优势。出现这种原因是构建二叉树的过程中没有平衡节点的左右子树的高度。根节点7的右子树有很高的深度,但是左子树是空的。我们需要的是一棵左右节点平衡的二叉树,而其中一种传统的平衡二叉树是AVL树。

  树的高度可以看做是节点与最低的叶子节点的距离。根节点的高度最大,而叶子节点的高度为0,一个不存在的节点的高度定义为-1

  AVL树的定义:AVL树是二叉树,其各个节点的左右子树的高度相差不超过1。

  AVL树是最早提出的自平衡二叉树,在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。AVL树得名于它的发明者G.M. Adelson-Velsky和E.M. Landis。AVL树种查找、插入和删除在平均和最坏情况下都是O(log n),增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

二、AVL树的构建

1、基本术语

  有四种情况可能导致二叉查找树不平衡,分别为:

  (1)左左型LL:插入一个新节点到根节点的左子树(Left)的左子树(Left),导致根节点的平衡因子由1变为2。

  (2)右右型RR:插入一个新节点到根节点的右子树(Right)的右子树(Right),导致根节点的平衡因子由-1变为-2。

  (3)左右型LR:插入一个新节点到根节点的左子树(Left)的右子树(Right),导致根节点的平衡因子由1变为2

  (4)右左型RL:插入一个新节点到根节点的右子树(Right)的左子树(Left),导致根节点的平衡因子由-1变为-2
这里写图片描述

2、AVL树的旋转

  AVL树的基本操作是旋转,有四种旋转方式,分别为:左旋转,右旋转,左右旋转(先左后右),右左旋转(先右后左),实际上,这四种旋转操作两两对称,因而也可以说成两类旋转操作。

(1)左左型LL

  LL情况需要右旋解决,如下图所示:
这里写图片描述

//右旋转
AvlNode RightRotate(AvlNode &k2)
{
    AvlNode k1 = k2->left;
    k2->left = k1->right;
    k1->right = k2;
    k2->height = max(height_tree(k2->left), height_tree(k2->right)) + 1;
    k1->height = max(height_tree(k1->left), k2->height) + 1;
    k2 = k1;
    return k2;
}

(2)右右型RR

  RR情况需要左旋解决,如下图所示:
这里写图片描述

//左旋转
AvlNode LeftRotate(AvlNode &k1)
{
    AvlNode k2 = k1->right;
    k1->right = k2->left;
    k2->left = k1;
    k1->height = max(height_tree(k1->right), height_tree(k1->left)) + 1;
    k2->height = max(height_tree(k2->right), k1->height) + 1;
    k1 = k2;
    return k1;
}

(3)左右型LR

  LR情况需要左右(先B左旋转,后A右旋转)旋解决,如下图所示:
这里写图片描述

//左右旋转(先左旋再右旋)
AvlNode LeftRightRotate(AvlNode &k)
{
    k->left = LeftRotate(k->left);
    return RightRotate(k);
}

(4)右左型RL

  RL情况需要右左旋解决(先B右旋转,后A左旋转),如下图所示:
这里写图片描述

//右左旋转(先右旋在左旋)
AvlNode RightLeftRotate(AvlNode &k)
{
    k->right = RightRotate(k->right);
    return LeftRotate(k);
}

3、AVL树节点的插入

//插入节点
AvlNode insert_tree(AvlNode &ptr, int val)
{
    if (ptr == nullptr)
    {
        ptr = new AVL_Tree;
        ptr->data = val;
        ptr->left = nullptr;
        ptr->right = nullptr;
        ptr->height = 0;
        return ptr;
    }
    else if (val < ptr->data)
    {
        ptr->left = insert_tree(ptr->left, val);
        if (height_tree(ptr->left)-height_tree(ptr->right) == 2)
            if (val < ptr->left->data)
                RightRotate(ptr);//左左型(LL)需要右旋转
            else
                LeftRightRotate(ptr);//左右型(LR)需要左右旋转
    }
    else if (val > ptr->data)
    {
        ptr->right = insert_tree(ptr->right, val);
        if (height_tree(ptr->right)-height_tree(ptr->left) == 2)
            if (val > ptr->right->data)
                LeftRotate(ptr);//右右型(RR)需要左旋转
            else
                RightLeftRotate(ptr);//右左型(RL)需要右左旋转
    }
    else ;
    ptr->height = max(height_tree(ptr->left), height_tree(ptr->right)) + 1;
    return ptr;
}

//打印所有节点
void print_tree(AvlNode &root)  
{
    if(root != nullptr)
    {
        print_tree(root->left);
        cout << root->data << " ";
        print_tree(root->right);
    }
}

int main(int argc, char const *argv[])
{
    vector<int> vec= {7,1,4,2,8,13,12,11,15,9,5};
    AvlNode ptr = nullptr;
    const int sz = vec.size();
    for (int i = 0; i < sz; ++i)
    {
        insert_tree(ptr, vec[i]);
        cout << vec[i] << " ";
    }
    cout << endl;
    print_tree(ptr);
    return 0;
}

参考:http://dongxicheng.org/structure/avl/
http://www.cnblogs.com/yonghao/p/5204848.html

猜你喜欢

转载自blog.csdn.net/daaikuaichuan/article/details/80397351
今日推荐