平衡二叉树

文章参考什么是平衡二叉树(AVL)一步一步写平衡二叉树(AVL树)

为什么要有平衡二叉树

​ 二叉搜索树一定程度上可以提高搜索效率,但是当原序列有序时,例如序列 A = {1,2,3,4,5,6},构造二叉搜索树如图 1.1。依据此序列构造的二叉搜索树为右斜树,同时二叉树退化成单链表,搜索效率降低为 O(n)。

在这里插入图片描述

在此二叉搜索树中查找元素 6 需要查找 6 次。

​ 二叉搜索树的查找效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列 A,将其改为下图的方式存储,查找元素 6 时只需比较 3 次,查找效率提升一倍。
在这里插入图片描述

​ 可以看出当节点数目一定,保持树的左右两端保持平衡,树的查找效率最高。这种左右子树的高度相差不超过 1 的树为平衡二叉树。

定义

​ 平衡二叉查找树:简称平衡二叉树。由前苏联的数学家 Adelse-Velskil 和 Landis 在 1962 年提出的高度平衡的二叉树,根据科学家的英文名也称为 AVL 树。它具有如下几个性质:

  1. 可以是空树。
  2. 假如不是空树,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。

平衡之意,如天平,即两边的分量大约相同。

例如下图不是平衡二叉树,因为结点 60 的左子树不是平衡二叉树。

在这里插入图片描述

也不是平衡二叉树,因为虽然任何一个结点的左子树与右子树都是平衡二叉树,但高度之差已经超过 1 。

在这里插入图片描述

下图也不是平衡二叉树。

在这里插入图片描述

平衡因子

定义:某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor),平衡二叉树中不存在平衡因子大于 1 的节点。在一棵平衡二叉树中,节点的平衡因子只能取 0 、1 或者 -1 ,分别对应着左右子树等高,左子树比较高,右子树比较高。

节点结构

定义平衡二叉树的节点结构:

//AVL树节点信息
template<class T>
class TreeNode
{
    public:
        TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
        T data;//值
        int hgt;//以此节点为根的树的高度
        unsigned int freq;//频率
        TreeNode* lson;//指向左儿子的地址
        TreeNode* rson;//指向右儿子的地址
};

AVL树插入时的失衡与调整

下图是一颗平衡二叉树

在这里插入图片描述

在此平衡二叉树插入节点 99 ,树结构变为:

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210302165145192.png#pic_center

在下图中,节点 66 的左子树高度为 1,右子树高度为 3,此时平衡因子为 -2,树失去平衡。以节点 66 为父节点的那颗树就称为 最小失衡子树。

在这里插入图片描述

最小失衡子树:在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树。

平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的。根据旋转的方向有两种处理方式,左旋 与 右旋 。

旋转的目的就是减少高度,通过降低整棵树的高度来平衡。哪边的树高,就把那边的树向上旋转。

左旋

在这里插入图片描述

以上图为例,加入新节点 99 后, 节点 66 的左子树高度为 1,右子树高度为 3,此时平衡因子为 -2。为保证树的平衡,此时需要对节点 66 做出旋转,因为右子树高度高于左子树,对节点进行左旋操作,流程如下:

(1)节点的右孩子替代此节点位置 (2)右孩子的左子树变为该节点的右子树 (3)节点本身变为右孩子的左子树

整个操作流程如动图 所示。

在这里插入图片描述

节点的右孩子替代此节点位置 —— 节点 66 的右孩子是节点 77 ,将节点 77 代替节点 66 的位置
右孩子的左子树变为该节点的右子树 —— 节点 77 的左子树为节点 75,将节点 75 挪到节点 66 的右子树位置
节点本身变为右孩子的左子树 —— 节点 66 变为了节点 77 的左子树

右旋

右旋操作与左旋类似,操作流程为:

(1)节点的左孩子代表此节点 (2)节点的左孩子的右子树变为节点的左子树 (3)将此节点作为左孩子节点的右子树。

在这里插入图片描述

AVL树的四种插入节点方式

A的左孩子的左子树插入节点(LL)

在这里插入图片描述

只需要执行一次右旋即可。

A的右孩子的右子树插入节点(RR)

在这里插入图片描述

只需要执行一次左旋即可。

A的左孩子的右子树插入节点(LR)

若 A 的左孩子节点 B 的右子树 E 插入节点 F ,导致节点 A 失衡,如图:

在这里插入图片描述

A 的平衡因子为 2 ,若仍按照右旋调整,则变化后的图形为这样:

在这里插入图片描述

经过右旋调整发现,调整后树仍然失衡,说明这种情况单纯的进行右旋操作不能使树重新平衡。那么这种插入方式需要执行两步操作,使得旋转之后为 原来根结点的左孩子的右孩子作为新的根节点 。

(1)对失衡节点 A 的左孩子 B 进行左旋操作,即上述 RR 情形操作。 (2)对失衡节点 A 做右旋操作,即上述 LL 情形操作。

调整过程如下:

在这里插入图片描述
在这里插入图片描述

也就是说,经过这两步操作,使得 原来根节点的左孩子的右孩子 E 节点成为了新的根节点。

A的右孩子的左子树插入节点(RL)

右孩子插入左节点的过程与左孩子插入右节点过程类似,也是需要执行两步操作,使得旋转之后为 原来根结点的右孩子的左孩子作为新的根节点 。

(1)对失衡节点 A 的右孩子 C 进行右旋操作,即上述 LL 情形操作。 (2)对失衡节点 A 做左旋操作,即上述 RR 情形操作。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

也就是说,经过这两步操作,使得 原来根节点的右孩子的左孩子 D 节点成为了新的根节点。

AVL树的四种删除节点方式

AVL 树和二叉查找树的删除操作情况一致,都分为四种情况:

(1)删除叶子节点 (2)删除的节点只有左子树 (3)删除的节点只有右子树 (4)删除的节点既有左子树又有右子树

只不过 AVL 树在删除节点后需要重新检查平衡性并修正,同时,删除操作与插入操作后的平衡修正区别在于,插入操作后只需要对插入栈中的弹出的第一个非平衡节点进行修正,而删除操作需要修正栈中的所有非平衡节点。

删除操作的大致步骤如下:

以前三种情况为基础尝试删除节点,并将访问节点入栈。
如果尝试删除成功,则依次检查栈顶节点的平衡状态,遇到非平衡节点,即进行旋转平衡,直到栈空。
如果尝试删除失败,证明是第四种情况。这时先找到被删除节点的右子树最小节点并删除它,将访问节点继续入栈。
再依次检查栈顶节点的平衡状态和修正直到栈空。
对于删除操作造成的非平衡状态的修正,可以这样理解:对左或者右子树的删除操作相当于对右或者左子树的插入操作,然后再对应上插入的四种情况选择相应的旋转就好了。

代码实现过程

第一步:节点信息

//AVL树节点信息
template<class T>
class TreeNode
{
    public:
        TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
        T data;//值
        int hgt;//以此节点为根的树的高度
        unsigned int freq;//频率
        TreeNode* lson;//指向左儿子的地址
        TreeNode* rson;//指向右儿子的地址
};

第二步:平衡二叉树类的声明

//AVL树类的属性和方法声明
template<typename T>
class AVLTree
{
    private:
        TreeNode<T>* root;//根节点
        void insertpri(TreeNode<T>* &node,T x);//插入
        TreeNode<T>* findpri(TreeNode<T>* node,T x);//查找
        void insubtree(TreeNode<T>* node);//中序遍历
        void Deletepri(TreeNode<T>* &node,T x);//删除
        int height(TreeNode<T>* node);//求树的高度
        void SingRotateLeft(TreeNode<T>* &k2);//左左情况下的旋转
        void SingRotateRight(TreeNode<T>* &k2);//右右情况下的旋转
        void DoubleRotateLR(TreeNode<T>* &k3);//左右情况下的旋转
        void DoubleRotateRL(TreeNode<T>* &k3);//右左情况下的旋转
        int Max(int cmpa,int cmpb);//求最大值

    public:
        AVLTree():root(nullptr){}
        void insert(T x);//插入接口
        TreeNode<T>* find(T x);//查找接口
        void Delete(T x);//删除接口
        void traversal();//遍历接口

};

第三步:两个辅助方法

旋转算法需要借助于两个功能的辅助,一个是求树的高度,一个是求两个高度的最大值。这里规定,一棵空树的高度为-1,只有一个根节点的树的高度为0,以后每多一层高度加1。为了解决指针NULL这种情况,写了一个求高度的函数,这个函数还是很有必要的。

//计算以节点为根的树的高度
template<typename T>
int AVLTree<T>::height(TreeNode<T> *node)
{
    if(node != nullptr)
    {
        return node->hgt;

    }
    return -1;
}
template<typename T>
int AVLTree<T>::Max(int cmpa, int cmpb)
{
    return cmpa>cmpb?cmpa:cmpb;
}

第四步:旋转

对于一个平衡的节点,由于任意节点最多有两个儿子,因此高度不平衡时,此节点的两颗子树的高度差2.容易看出,这种不平衡出现在下面四种情况:

在这里插入图片描述

1、6节点的左子树3节点高度比右子树7节点大2,左子树3节点的左子树1节点高度大于右子树4节点,这种情况成为左左。

2、6节点的左子树2节点高度比右子树7节点大2,左子树2节点的左子树1节点高度小于右子树4节点,这种情况成为左右。

3、2节点的左子树1节点高度比右子树5节点小2,右子树5节点的左子树3节点高度大于右子树6节点,这种情况成为右左。

4、2节点的左子树1节点高度比右子树4节点小2,右子树4节点的左子树3节点高度小于右子树6节点,这种情况成为右右。

从图中可以可以看出,1和4两种情况是对称的,这两种情况的旋转算法是一致的,只需要经过一次旋转就可以达到目标,我们称之为单旋转。2和3两种情况也是对称的,这两种情况的旋转算法也是一致的,需要进行两次旋转,我们称之为双旋转。

第五步:单旋转

//左左情况下的旋转
template<typename T>
void AVLTree<T>::SingRotateLeft(TreeNode<T> *&k2)
{
    //不平衡节点变成左节点的右节点,之前左节点的右节点成为不平衡节点的左节点
    //对于高度的改变只有不平衡节点及其左节点
    TreeNode<T>* k1;
    k1 = k2->lson;
    k2->lson = k1->rson;
    k1->rson = k2;
    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
    k2 = k1;

}

template<typename T>
void AVLTree<T>::SingRotateRight(TreeNode<T> *&k2)
{
    //不平衡节点变成右节点的左节点,之前右节点的左节点成为不平衡节点的右节点
    //对于高度的改变只有不平衡节点及其右节点
    TreeNode<T>* k1;
    k1 = k2->rson;
    k2->rson = k1->lson;
    k1->lson = k2;

    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
    k2 = k1;

}

第六步:双旋转

//左右旋转
template<typename T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T> *&k3)
{
    //先变化左左,再进行单次旋转

    SingRotateRight(k3->lson);
    SingRotateLeft(k3);
}

template<typename T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T> *&k3)
{
    //先变化右右,再进行单次旋转
    SingRotateLeft(k3->rson);
    SingRotateRight(k3);
}

第七步:插入

template<typename T>
void AVLTree<T>::insertpri(TreeNode<T> *&node, T x)
{

    //插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径
    //,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
    if(node == nullptr)//如果节点为空,就在此节点处加入x信息
    {
        node = new TreeNode<T>();
        node->data = x;
        return;
    }
    if(node->data > x)//如果x小于节点的值,就继续在节点的左子树中插入x
    {
        insertpri(node->lson,x);
        node->hgt = Max(height(node->lson),height(node->rson)) + 1;

        if(2 == height(node->lson)-height(node->rson))//如果高度之差为2的话就失去了平衡,需要旋转
        {
            if(x < node->lson->data)
                SingRotateLeft(node);
            else
                DoubleRotateLR(node);
        }

    }
    else if(node->data < x)//如果x大于节点的值,就继续在节点的右子树中插入x
    {
        insertpri(node->rson,x);
        node->hgt = Max(height(node->lson),height(node->rson)) + 1;

        if(2 == height(node->rson)-height(node->lson))//如果高度之差为2的话就失去了平衡,需要旋转
        {
            if(x>node->rson->data)
                SingRotateRight(node);
            else
                DoubleRotateRL(node);
        }

    }
    else
    {
        ++(node->freq);//如果相等,就把频率加1
    }

}

template<typename T>
void AVLTree<T>::insert(T x)
{
   insertpri(root,x);
}

第八步:查找

template<typename T>
TreeNode<T> *AVLTree<T>::findpri(TreeNode<T> *node, T x)
{
    //和二叉查找树相比,查找方法没有变法,不过根据存储的特性
    //AVL树能维持在一个O(logN)的稳定的时间,而二叉查找树则相当不稳定。

    if(node == nullptr)//如果节点为空说明没找到,返回NULL
    {
        return nullptr;
    }
    if(node->data > x)//如果x小于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->lson,x);
    }
    else if(node->data < x)//如果x大于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->rson,x);
    }
    else
        return node;//如果相等,就找到了此节点
}

template<typename T>
void AVLTree<T>::insubtree(TreeNode<T> *node)
{
    if(node== nullptr)
        return;
    insubtree(node->lson);//先遍历左子树
    std::cout<<node->data<<" ";//输出根节点
    insubtree(node->rson);//再遍历右子树
}

template<typename T>
void AVLTree<T>::Deletepri(TreeNode<T> *&node, T x)
{
    if(node==NULL) return;//没有找到值是x的节点
    if(x < node->data)
    {
        Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
        if(2 == height(node->rson)-height(node->lson))
        {
            if(node->rson->lson != NULL && (height(node->rson->lson)>height(node->rson->rson)))
                DoubleRotateRL(node);
            else
                SingRotateRight(node);
        }

    }

    else if(x > node->data)
    {
        Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
        if(2 == height(node->lson)-height(node->rson))
        {
            if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                DoubleRotateLR(node);
            else
                SingRotateLeft(node);
        }

    }
    else//如果相等,此节点就是要删除的节点
    {
        if(node->lson && node->rson)//此节点有两个儿子
        {
            TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
            while(temp->lson != NULL) temp = temp->lson;//找到右子树中值最小的节点
            //把右子树中最小节点的值赋值给本节点
            node->data = temp->data;
            node->freq = temp->freq;
            Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
            if(2 == height(node->lson)- height(node->rson))
            {
                if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                    DoubleRotateLR(node);
                else
                    SingRotateLeft(node);
            }
        }
        else//此节点有1个或0个儿子
        {
            TreeNode<T>* temp = node;
            if(node->lson == NULL)//有右儿子或者没有儿子
                node = node->rson;
            else if(node->rson == NULL)//有左儿子
                node = node->lson;
            delete(temp);
            temp=NULL;
        }
    }

    if(node == NULL)
        return;
    node->hgt = Max(height(node->lson),height(node->rson))+1;
    return;
}

template<typename T>
TreeNode<T> *AVLTree<T>::find(T x)
{
    return findpri(root,x);
}

第九步:删除

template<typename T>
void AVLTree<T>::Deletepri(TreeNode<T> *&node, T x)
{
    if(node==NULL) return;//没有找到值是x的节点
    if(x < node->data)
    {
        Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
        if(2 == height(node->rson)-height(node->lson))
        {
            if(node->rson->lson != NULL && (height(node->rson->lson)>height(node->rson->rson)))
                DoubleRotateRL(node);
            else
                SingRotateRight(node);
        }

    }

    else if(x > node->data)
    {
        Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
        if(2 == height(node->lson)-height(node->rson))
        {
            if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                DoubleRotateLR(node);
            else
                SingRotateLeft(node);
        }

    }
    else//如果相等,此节点就是要删除的节点
    {
        if(node->lson && node->rson)//此节点有两个儿子
        {
            TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
            while(temp->lson != NULL) temp = temp->lson;//找到右子树中值最小的节点
            //把右子树中最小节点的值赋值给本节点
            node->data = temp->data;
            node->freq = temp->freq;
            Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
            if(2 == height(node->lson)- height(node->rson))
            {
                if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                    DoubleRotateLR(node);
                else
                    SingRotateLeft(node);
            }
        }
        else//此节点有1个或0个儿子
        {
            TreeNode<T>* temp = node;
            if(node->lson == NULL)//有右儿子或者没有儿子
                node = node->rson;
            else if(node->rson == NULL)//有左儿子
                node = node->lson;
            delete(temp);
            temp=NULL;
        }
    }

    if(node == NULL)
        return;
    node->hgt = Max(height(node->lson),height(node->rson))+1;
    return;
}

template<typename T>
void AVLTree<T>::Delete(T x)
{
    Deletepri(root,x);
}

第十步:中序遍历

//中序遍历函数
template<class T>
void AVLTree<T>::insubtree(TreeNode<T>* node)
{
    if(node==NULL) return;
    insubtree(node->lson);//先遍历左子树
    cout<<node->data<<" ";//输出根节点
    insubtree(node->rson);//再遍历右子树
}
//中序遍历接口
template<class T>
void AVLTree<T>::traversal()
{
    insubtree(root);
}

完整代码

#ifndef BTREE_H
#define BTREE_H
#include <stdlib.h>
using namespace std;
//AVL树节点信息
template<class T>
class TreeNode
{
    public:
        TreeNode():lson(NULL),rson(NULL),freq(1),hgt(0){}
        T data;//值
        int hgt;//以此节点为根的树的高度
        unsigned int freq;//频率
        TreeNode* lson;//指向左儿子的地址
        TreeNode* rson;//指向右儿子的地址
};

//AVL树类的属性和方法声明
template<typename T>
class AVLTree
{
    private:
        TreeNode<T>* root;//根节点
        void insertpri(TreeNode<T>* &node,T x);//插入
        TreeNode<T>* findpri(TreeNode<T>* node,T x);//查找
        void insubtree(TreeNode<T>* node);//中序遍历
        void Deletepri(TreeNode<T>* &node,T x);//删除
        int height(TreeNode<T>* node);//求树的高度
        void SingRotateLeft(TreeNode<T>* &k2);//左左情况下的旋转
        void SingRotateRight(TreeNode<T>* &k2);//右右情况下的旋转
        void DoubleRotateLR(TreeNode<T>* &k3);//左右情况下的旋转
        void DoubleRotateRL(TreeNode<T>* &k3);//右左情况下的旋转
        int Max(int cmpa,int cmpb);//求最大值

    public:
        AVLTree():root(nullptr){}
        void insert(T x);//插入接口
        TreeNode<T>* find(T x);//查找接口
        void Delete(T x);//删除接口
        void traversal();//遍历接口

};
template<typename T>
void AVLTree<T>::insertpri(TreeNode<T> *&node, T x)
{

    //插入的方法和二叉查找树基本一样,区别是,插入完成后需要从插入的节点开始维护一个到根节点的路径
    //,每经过一个节点都要维持树的平衡。维持树的平衡要根据高度差的特点选择不同的旋转算法。
    if(node == nullptr)//如果节点为空,就在此节点处加入x信息
    {
        node = new TreeNode<T>();
        node->data = x;
        return;
    }
    if(node->data > x)//如果x小于节点的值,就继续在节点的左子树中插入x
    {
        insertpri(node->lson,x);
        node->hgt = Max(height(node->lson),height(node->rson)) + 1;

        if(2 == height(node->lson)-height(node->rson))//如果高度之差为2的话就失去了平衡,需要旋转
        {
            if(x < node->lson->data)
                SingRotateLeft(node);
            else
                DoubleRotateLR(node);
        }

    }
    else if(node->data < x)//如果x大于节点的值,就继续在节点的右子树中插入x
    {
        insertpri(node->rson,x);
        node->hgt = Max(height(node->lson),height(node->rson)) + 1;

        if(2 == height(node->rson)-height(node->lson))//如果高度之差为2的话就失去了平衡,需要旋转
        {
            if(x>node->rson->data)
                SingRotateRight(node);
            else
                DoubleRotateRL(node);
        }

    }
    else
    {
        ++(node->freq);//如果相等,就把频率加1
    }

}

template<typename T>
TreeNode<T> *AVLTree<T>::findpri(TreeNode<T> *node, T x)
{
    //和二叉查找树相比,查找方法没有变法,不过根据存储的特性
    //AVL树能维持在一个O(logN)的稳定的时间,而二叉查找树则相当不稳定。

    if(node == nullptr)//如果节点为空说明没找到,返回NULL
    {
        return nullptr;
    }
    if(node->data > x)//如果x小于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->lson,x);
    }
    else if(node->data < x)//如果x大于节点的值,就继续在节点的左子树中查找x
    {
        return findpri(node->rson,x);
    }
    else
        return node;//如果相等,就找到了此节点
}

template<typename T>
void AVLTree<T>::insubtree(TreeNode<T> *node)
{
    if(node== nullptr)
        return;
    insubtree(node->lson);//先遍历左子树
    std::cout<<node->data<<" ";//输出根节点
    insubtree(node->rson);//再遍历右子树
}

template<typename T>
void AVLTree<T>::Deletepri(TreeNode<T> *&node, T x)
{
    if(node==NULL) return;//没有找到值是x的节点
    if(x < node->data)
    {
        Deletepri(node->lson,x);//如果x小于节点的值,就继续在节点的左子树中删除x
        if(2 == height(node->rson)-height(node->lson))
        {
            if(node->rson->lson != NULL && (height(node->rson->lson)>height(node->rson->rson)))
                DoubleRotateRL(node);
            else
                SingRotateRight(node);
        }

    }

    else if(x > node->data)
    {
        Deletepri(node->rson,x);//如果x大于节点的值,就继续在节点的右子树中删除x
        if(2 == height(node->lson)-height(node->rson))
        {
            if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                DoubleRotateLR(node);
            else
                SingRotateLeft(node);
        }

    }
    else//如果相等,此节点就是要删除的节点
    {
        if(node->lson && node->rson)//此节点有两个儿子
        {
            TreeNode<T>* temp=node->rson;//temp指向节点的右儿子
            while(temp->lson != NULL) temp = temp->lson;//找到右子树中值最小的节点
            //把右子树中最小节点的值赋值给本节点
            node->data = temp->data;
            node->freq = temp->freq;
            Deletepri(node->rson,temp->data);//删除右子树中最小值的节点
            if(2 == height(node->lson)- height(node->rson))
            {
                if(node->lson->rson != NULL && (height(node->lson->rson)>height(node->lson->lson)))
                    DoubleRotateLR(node);
                else
                    SingRotateLeft(node);
            }
        }
        else//此节点有1个或0个儿子
        {
            TreeNode<T>* temp = node;
            if(node->lson == NULL)//有右儿子或者没有儿子
                node = node->rson;
            else if(node->rson == NULL)//有左儿子
                node = node->lson;
            delete(temp);
            temp=NULL;
        }
    }

    if(node == NULL)
        return;
    node->hgt = Max(height(node->lson),height(node->rson))+1;
    return;
}
//计算以节点为根的树的高度
template<typename T>
int AVLTree<T>::height(TreeNode<T> *node)
{
    if(node != nullptr)
    {
        return node->hgt;

    }
    return -1;
}
//左左情况下的旋转
template<typename T>
void AVLTree<T>::SingRotateLeft(TreeNode<T> *&k2)
{
    //不平衡节点变成左节点的右节点,之前左节点的右节点成为不平衡节点的左节点
    //对于高度的改变只有不平衡节点及其左节点
    TreeNode<T>* k1;
    k1 = k2->lson;
    k2->lson = k1->rson;
    k1->rson = k2;
    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
    k2 = k1;

}

template<typename T>
void AVLTree<T>::SingRotateRight(TreeNode<T> *&k2)
{
    //不平衡节点变成右节点的左节点,之前右节点的左节点成为不平衡节点的右节点
    //对于高度的改变只有不平衡节点及其右节点
    TreeNode<T>* k1;
    k1 = k2->rson;
    k2->rson = k1->lson;
    k1->lson = k2;

    k2->hgt=Max(height(k2->lson),height(k2->rson))+1;
    k1->hgt=Max(height(k1->lson),k2->hgt)+1;
    k2 = k1;

}
//左右旋转
template<typename T>
void AVLTree<T>::DoubleRotateLR(TreeNode<T> *&k3)
{
    //先变化左左,再进行单次旋转

    SingRotateRight(k3->lson);
    SingRotateLeft(k3);
}

template<typename T>
void AVLTree<T>::DoubleRotateRL(TreeNode<T> *&k3)
{
    //先变化右右,再进行单次旋转
    SingRotateLeft(k3->rson);
    SingRotateRight(k3);
}

template<typename T>
int AVLTree<T>::Max(int cmpa, int cmpb)
{
    return cmpa>cmpb?cmpa:cmpb;
}

template<typename T>
void AVLTree<T>::insert(T x)
{
   insertpri(root,x);
}

template<typename T>
TreeNode<T> *AVLTree<T>::find(T x)
{
    return findpri(root,x);
}

template<typename T>
void AVLTree<T>::Delete(T x)
{
    Deletepri(root,x);
}

template<typename T>
void AVLTree<T>::traversal()
{
    insubtree(root);
}

#endif // BTREE_H

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值