4、【数据结构】树形结构之平衡二叉查找树(AVL树)

一、AVL树简介

1、定义

    AVL树是高度平衡的二叉查找树,它的特点是:AVL树中任何结点的两个子树的高度最大差别为1。
AVL树的示意图如下:

《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》

1.1 结点定义
typedef int DataType;

struct Node
{
    DataType key;
    int height;
    Node *lchild;
    Node *rchild;

    Node(DataType value, Node *l, Node *r)
        :key(value), height(0), lchild(l), rchild(r) {}
};
1.2 AVL树的定义
class AVLTree
{
private:
    Node *root;

public:
    AVLTree();
    ~AVLTree();

    //外部接口函数定义
    //获取树的高度
    int height();

    //前序遍历
    void preOrder();
    //中序遍历
    void inOrder();
    //后序遍历
    void postOrder();
    //分层遍历
    void levelOrder();

    //查找AVL树中键值为key的节点
    Node *searchNode(DataType key);

    //查找最小节点:返回最小节点的键值
    DataType minmum();
    //查找最大节点:返回最大节点的键值
    DataType maxmum();

    //将节点(键值为key)插入到AVL树中
    void insertNode(DataType key);
    //删除键值为key的节点
    void removeNode(DataType key);

    //销毁AVL树
    void destroyAVLTree();

    //打印AVL树
    void printAVLTree();
    //定义AVL内部接口函数
private:
    //获取树的高度
    int height(Node *root);

    //前序遍历
    void preOrder(Node *root) const;
    //中序遍历
    void inOrder(Node *root) const;
    //后序遍历
    void postOrder(Node *root) const;
    //分层遍历
    void levelOrder(Node *root) const;

    //查找AVL树中键值为key的节点
    Node *searchNode(Node *root, DataType key) const;
    //查找最小节点,返回最小节点
    Node *minmum(Node *root);
    //查找最大节点,返回最大节点
    Node *maxmum(Node *root);

    //LL:左单旋转,返回旋转后的根节点
    Node *leftLeftRotation(Node *k2);
    //RR:右单旋转,返回旋转后的根节点
    Node *rightRightRotation(Node *k1);
    //LR:左双旋转
    Node *leftRightRotation(Node *k3);
    //RL:右双旋转
    Node *rightLeftRotation(Node *k1);

    //将键值为key的节点z插入到AVL树中
    Node *insertNode(Node *&root, DataType key);

    //删除AVL树中的节点z,并返回被删除的节点
    Node *removeNode(Node *root, Node *z);

    //销毁AVL树
    void destroyAVLTree(Node *root);
    //打印AVL树
    void printAVLTree(Node *root, DataType key, int direction);
};
1.3 树的高度
//获取树的高度 - 内部函数
int AVLTree::height(Node *avlroot)
{
    if(avlroot)
        return avlroot->height;
    return 0;
}
//获取速度额高度 - 外部函数
int AVLTree::height()
{
    return height(root);
}

    AVL树的高度的定义:树的高度为最大层次。即空的二叉树的高度为0,非空树的高度等于它的最大层次(根的层次为1,根的子结点为第2层,以此类推)。

2、AVL树的时间复杂度分析

    AVL树的查找、插入和删除在平均和最坏情况下都是O(logn)。

二、AVL树相关操作

    如果在AVL树中插入或删除节点后,使得高度之差大于1。此时,AVL树的平衡状态就被破坏,它就不再是一棵二叉树;为了让它重新维持在一个平衡状态,就需要对其进行旋转处理。

1、AVL树失衡分类

    如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。它们都有各自的定义:
    (1) LL:LeftLeft,也称为”左左”。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致”根的左子树的高度”比”根的右子树的高度”大2,导致AVL树失去了平衡。
    (2) LR:LeftRight,也称为”左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致”根的左子树的高度”比”根的右子树的高度”大2,导致AVL树失去了平衡。
    (3) RL:RightLeft,称为”右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致”根的右子树的高度”比”根的左子树的高度”大2,导致AVL树失去了平衡。
    (4) RR:RightRight,称为”右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致”根的右子树的高度”比”根的左子树的高度”大2,导致AVL树失去了平衡。

2、AVL树的旋转操作

    AVL失去平衡之后,可以通过旋转使其恢复平衡,下面分别介绍”LL(左左),LR(左右),RR(右右)和RL(右左)”这4种情况对应的旋转方法。

2.1 LL旋转

《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     对于LL旋转,你可以这样理解为:LL旋转是围绕”失去平衡的AVL根节点”进行的,也就是节点k2;而且由于是LL情况,即左左情况,就用手抓着”左孩子,即k1″使劲摇。将k1变成根节点,k2变成k1的右子树,”k1的右子树”变成”k2的左子树”。 LL选旋转实现代码如下:

//返回值为旋转后的根节点
Node *AVLTree::leftLeftRotation(Node *k2)
{
    Node *k1;

    k1 = k2->lchild;
    k2->lchild = k1->rchild;
    k1->rchild = k2;

    k2->height = max( height(k2->lchild), height(k2->rchild)) + 1;
    k1->height = max( height(k1->lchild), k2->height) + 1;

    return k1;
}
2.2 RR旋转

《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     理解了LL之后,RR就容易理解了,RR是与LL对称的。RR旋转代码实现如下:

//返回值为旋转后的根节点
Node *AVLTree::rightRightRotation(Node *k1)
{
    Node *k2;

    k2 = k1->rchild;
    k1->rchild = k2->lchild;
    k2->lchild = k1;

    k1->height = max( height(k1->lchild), height(k1->rchild)) + 1;
    k2->height = max( height(k2->rchild), k1->height) + 1;
    return k2;
}

    LL失衡和RR失衡的情况只需要一次旋转就可以是AVL树恢复平衡,当LR失衡和RL失衡需要两次旋转才能使AVL树恢复平衡。

2.3 LR旋转

《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     上图展示的是AVL树LR失衡的情况。LR失衡的AVL树需要两次旋转才能使AVL树恢复平衡,第一次旋转是围绕k1进行“RR”旋转,旋转结果如下:
《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     第二次旋转是围绕k2进行“LL”旋转,旋转结果如下:
《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     LR旋转的代码实现如下:

//返回值为旋转后的根节点
Node *AVLTree::leftRightRotation(Node *k3)
{
    k3->lchild = rightRightRotation(k3->lchild);
    return leftLeftRotation(k3);
}
2.4 RL旋转

    RL旋转是与LR旋转对称的情况,同样需要两次旋转才能使AVL树恢复平衡。具体过程如下:

《4、【数据结构】树形结构之平衡二叉查找树(AVL树)》     第一次旋转是围绕k3进行“LL”旋转,第二次是围绕k1进行“RR”旋转。RL旋转的代码实现如下:

//返回值为旋转后的根节点
Node *AVLTree::rightLeftRotation(Node *k1)
{
    k1->rchild = leftLeftRotation(k1->rchild);
    return rightRightRotation(k1);
}
3、AVL树的插入操作

    插入结点的代码实现如下:

//tree为AVL树的根结点
//key为要插入的结点的键值
//返回值为:根结点
Node *AVLTree::insertNode(Node *&avlroot, DataType key)
{
    if(avlroot == NULL)
    {
        //新建节点
        avlroot = new Node(key, NULL, NULL);
        if(avlroot == NULL)
        {
            cout << "creat avltree node failed" << endl;
            return NULL;
        }
    }
    else if(key < avlroot->key)//应该将可以插入到avltree的左子树的情况
    {
        avlroot->lchild = insertNode(avlroot->lchild, key);
        //插入节点后,若AVL树失去平衡,则进行相应的调节
        if(height(avlroot->lchild) -height(avlroot->rchild) == 2)
        {
            if(key < avlroot->lchild->key)
                avlroot = leftLeftRotation(avlroot);
            else
                avlroot = leftRightRotation(avlroot);
        }
    }
    else if(key > avlroot->key)//应该将key插入到AVLTree的右子树的情况
    {
        avlroot->rchild = insertNode(avlroot->rchild, key);
        if(height(avlroot->rchild) - height(avlroot->lchild) == 2)
        {
            if(key > avlroot->rchild->key)
                avlroot = rightRightRotation(avlroot);
            else
                avlroot = rightLeftRotation(avlroot);
        }
    }
    else //key == avlroot->key
    {
        cout << "添加失败" << endl;
    }
    avlroot->height = max(height(avlroot->lchild), height(avlroot->rchild)) + 1;
    return avlroot;
}
void AVLTree::insertNode(DataType key)
{
    insertNode(root, key);
}
4、AVL树的删除操作

    删除结点的代码实现如下:

//对内接口函数
//tree为AVL树的根节点
//z为要删除的结点
//返回值为根结点
Node *AVLTree::removeNode(Node *avlroot, Node *z)
{
    if(avlroot == NULL || z == NULL)
        return NULL;
    if(z->key < avlroot->key)
    {
        avlroot->lchild = removeNode(avlroot->lchild, z);
        //删除节点后,如果AVL树失去平衡则进行相应的调整
        if(height(avlroot->rchild) - height(avlroot->lchild) == 2)
        {
            Node *r = avlroot->rchild;
            if(height(r->lchild) > height(r->rchild))
                avlroot = rightLeftRotation(avlroot);
            else
                avlroot = rightRightRotation(avlroot);
        }
    }
    else if(z->key > avlroot->key)
    {
        avlroot->rchild = removeNode(avlroot->rchild, z);
        //删除节点后,若AVL树失去平衡,则进行相应的调整
        if(height(avlroot->lchild) - height(avlroot->rchild) == 2)
        {
            Node *l = avlroot->lchild;
            if(height(l->rchild) > height(l->lchild))
                avlroot = leftRightRotation(avlroot);
            else
                avlroot = leftLeftRotation(avlroot);
        }
    }
    else//删除根节点
    {
        //avlroot的左右孩子都非空
        if(avlroot->lchild != NULL && avlroot->rchild != NULL)
        {
            if(height(avlroot->lchild) > height(avlroot->rchild))
            {
                //如果avlroot的左子树比右子树高
                //则(1)找出avlroot的左子树中的最大节点(avlroot的前驱节点)
                //(2)将该最大节点赋值给avlroot
                //(3)删除该最大节点
                //这类似于用avlroot的左子树中的最大节点做avlroot的替身
                //采用这种方式的好处是:删除了avlroot的左子树中的最大节点后,AVL树仍是平衡的
                Node *max = maxmum(avlroot->lchild);
                avlroot->key = max->key;
                avlroot->lchild = removeNode(avlroot->lchild, max);
            }
            else
            {
                //若avlroot的左子树不比右子树高(即他们相等,或右子树比左子树高)
                //则(1)找出avlroot的右子树中的最小节点(avlroot的后继节点)
                //(2)将该最小节点的值赋值给avlroot
                //(3)删除该最小节点
                //这类似于用avlroot的右子树中的最小节点做avlroot的替身
                //采用这种方式的好处是:删除avlroot右子树中的最小节点后,AVL树仍是平衡的
                Node *min = minmum(avlroot->rchild);
                avlroot->key = min->key;
                avlroot->rchild = removeNode(avlroot->rchild, min);
            }
        }
        else //如果avlroot的左右子树中有一个为空
        {
            Node *temp = avlroot;
            avlroot = (avlroot->lchild != NULL) ? avlroot->lchild : avlroot->rchild;
            delete temp;
        }
    }
    return avlroot;
}
void AVLTree::removeNode(DataType key)
{
    Node *z;
    //如果键值为key的节点存在与AVL树中,则删除它,并返回删除后的根节点
    if((z = searchNode(root, key)) != NULL)
        root = removeNode(root, z);
}

    AVL树的遍历、最大值查找、最小值查找,打印、销毁等接口与“二叉查找树”的基本一样。
完整的示例代码
参考文章:http://www.cnblogs.com/skywang12345/p/3576969.html

    原文作者:AVL树
    原文地址: https://blog.csdn.net/sinat_33924041/article/details/83502054
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞