平衡二叉树

平衡二叉树(AVL树)

定义

  • 或是空树
  • 或是每个结点深度之差不超过1二叉排序树
  • 举例如下(结点的平衡因子=左子树深度-右子树深度):

在这里插入图片描述

旋转(将二叉排序树调整为二叉平衡树)

  • 由于在二叉平衡树中插入或删除结点后,可能使平衡二叉树失去平衡,因此需要对其进行调整。
  • 假设结点a是失去平衡的最小子树的根结点

1. 左单旋转(RR型)

  • 失衡原因:结点的右孩子的右子树插入结点所致
  • 调整策略:以结点的右孩子为轴,逆时针旋转,旋转以后,结点将作为其右孩子的左孩子,结点右孩子原来的左孩子作为结点的右孩子
  • 举例(B、D、E三课子树高度为h):

在这里插入图片描述

//左单旋转
//结点右孩子的右子树插入结点导致的失衡
//GetHeight函数:获取树的高度
template <typename T>
AvlNode<T>* AvlTree<T>::RR(AvlNode<T>* t)
{
    AvlNode<T>* q = t->right;
    t->right = q->left;
    q->left = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

2.右单旋转(LL型)

  • 失衡原因:结点的左孩子的左子树插入结点所致
  • 调整策略:以结点的左孩子为轴,顺时针旋转,旋转以后,结点将作为其左孩子的右孩子,结点左孩子原来的右孩子作为结点的左孩子
  • 举例(B、D、E三课子树高度为h)

在这里插入图片描述

//右单旋转
//结点左孩子的左子树插入结点导致的失衡
//GetHeight函数:获取树的高度
template <typename T>
AvlNode<T>* AvlTree<T>::LL(AvlNode<T>* t)
{
    AvlNode<T>* q = t->left;
    t->left = q->right;
    q->right = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

3.先左后右双向旋转(LR型)

  • 失衡原因:结点的左孩子的右子树插入结点所致
  • 调整策略:
    • 第一次,对以结点b为根结点的子树,以结点c为轴,逆时针旋转,将结点c的左子树E作为结点b的右子树,将结点b作为结点c的左子树
    • 第二次,以c为轴,顺时针旋转,c的右子树F作为a的左子树
  • 举例(D、G两颗子树高度为h;E、F两颗子树高度为h-1)

在这里插入图片描述

//先左后右双向旋转
//结点的左孩子的右子树插入结点导致的失衡
template <typename T>
AvlNode<T>* AvlTree<T>::LR(AvlNode<T>* t)
{
    //双旋转可以通过两次单旋转实现
    //对结点的左孩子进行RR旋转,再对结点进行LL旋转
    RR(t->left);
    return LL(t);
}

4.先右后左双向旋转(RL型)

  • 失衡原因:结点的右孩子的左子树插入结点所致
  • 调整策略:
    • 第一次,以结点c为轴,顺时针旋转
    • 第二次,以结点c为轴,逆时针旋转
  • 举例

在这里插入图片描述

//先右后左双向旋转
//结点的右孩子的左子树插入结点导致的失衡
template <typename T>
AvlNode<T>* AvlTree<T>::RL(AvlNode<T>* t)
{
    //双旋转可以通过两次单旋转实现
    //对结点的右孩子进行LL旋转,再对结点进行RR旋转
    LL(t->right);
    return RR(t);
}

获取树的高度

//获取树的高度
template <typename T>
int AvlTree<T>::GetHeight(AvlNode<T>* t)
{
    //空树
    if (t == nullptr)
        return -1;
    //非空树时,返回树的高度
    else
        return t->height;
}

查找最大、最小值结点

//查找最大值结点
template <typename T>
AvlNode<T>* AvlTree<T>::FindMax(AvlNode<T>* t) const
{
    //空树
    if (t == nullptr)
        return nullptr;
    //由于是二叉排序树,结点的右子树为空,直接返回该结点
    if (t->right == nullptr)
        return t;
    //若不符合上面两种情况,继续向结点右子树深入
    return FindMax(t->right);
}

//查找最小值结点
template <typename T>
AvlNode<T>* AvlTree<T>::FindMin(AvlNode<T>* t) const
{
    //空树
    if (t == nullptr)
        return nullptr;
    //由于是二叉排序树,结点的左子树为空,直接返回该结点
    if (t->left == nullptr)
        return t;
    //若不符合上面两种情况,继续向结点的左子树深入
    return FindMin(t->left);
}

插入结点

//插入结点
template <typename T>
void AvlTree<T>::Insert(AvlNode<T>*& t, T x)
{
    if (t == nullptr)//情况一:结点为空
        t = new AvlNode<T>(x);
    else if (x < t->data)//情况二:插入数据小于结点
    {
        Insert(t->left, x);
        //判断平衡
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {
            // 分两种情况
            // 插入结点在结点的左孩子的左子树上  左左 LL
            // 插入结点在结点的左孩子的右子树上  左右 LR         
            if (x < t->left->data)//左左
                t = LL(t);
            else                  //左右
                t = LR(t);
        }
    }
    else if (x > t->data)//情况三:插入数据大于结点
    {
        Insert(t->right, x);
        //判断平衡
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            // 分两种情况
            // 插入结点在结点的右孩子的左子树上  左左 RL
            // 插入结点在结点的右孩子的右子树上  左右 RR  
            if (x > t->right->data)
                t = RR(t);
            else
                t = RL(t);
        }
    }
    else //情况四:插入数据已经存在结点
        ;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}

删除结点

// 删除结点
template <typename T>
bool AvlTree<T>::Delete(AvlNode<T>*& t, T x)
{
    //t为空 要删除的结点未找到  返回false
    if (t == nullptr)
        return false;
    //要删除的结点找到了
    else if (t->data == x)
    {
        //左右子树都非空
        if (t->left != nullptr && t->right != nullptr)
        {//在高度更大的那个子树上进行删除操作

            //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
            if (GetHeight(t->left) > GetHeight(t->right))
            {
                t->data = FindMax(t->left)->data;
                Delete(t->left, t->data);
            }
            else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
            {
                t->data = FindMin(t->right)->data;
                Delete(t->right, t->data);
            }
        }
        else
        {//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
            AvlNode<T>* old = t;
            t = t->left ? t->left : t->right;//t赋值为不空的子结点
            delete old;
        }
    }
    else if (x < t->data)//要删除的结点在左子树上
    {
        //递归删除左子树上的结点
        Delete(t->left, x);
        //判断是否仍然满足平衡条件
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            if (GetHeight(t->right->left) > GetHeight(t->right->right))
            {
                //RL双旋转
                t = RL(t);
            }
            else
            {//RR单旋转
                t = RR(t);
            }
        }
        else//满足平衡条件 调整高度信息
        {
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }
    else//要删除的结点在右子树上
    {
        //递归删除右子树结点
        Delete(t->right, x);
        //判断平衡情况
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {
            if (GetHeight(t->left->right) > GetHeight(t->left->left))
            {
                //LR双旋转
                t = LR(t);
            }
            else
            {
                //LL单旋转
                t = LL(t);
            }
        }
        else//满足平衡性 调整高度
        {
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }

    return true;
}

查找结点

//查找结点
template <typename T>
bool AvlTree<T>::Contains(AvlNode<T>* t, const T x) const
{
    if (t == nullptr)
        return false;
    if (x < t->data)
        return Contains(t->left, x);
    else if (x > t->data)
        return Contains(t->right, x);
    else
        return true;
}

完整代码

#include <iostream>
#include <algorithm>
using namespace std;
#pragma once

//AvlNode
template <typename T>
struct AvlNode
{
    T data;                 //数据域
    int height;             //结点所在高度
    AvlNode<T>* left;       //左孩子指针域    
    AvlNode<T>* right;      //右孩子指针域
    /*结构体初始化*/
    AvlNode<T>(const T value) : data(value), left(nullptr), right(nullptr), height(0) {}
};

//AvlTree
template <class T>
class AvlTree
{
public:
    //默认构造函数
    AvlTree<T>() {}        
    //析构函数
    ~AvlTree<T>() {}        
    /*
    * C++11标准
    * 当使用默认构造函数时,类内成员必须都有类内初始值
    * 当使用其他构造函数时,要将类内成员初始化,否则,即便该类内成员有类内初始值,其值也是未知的
    */
    //二叉树根结点
    AvlNode<T>* root{};//value is set via default member initialization
    //插入结点
    void Insert(AvlNode<T>*& t, T x);
    //删除结点
    bool Delete(AvlNode<T>*& t, T x);
    /*
    * 成员函数后加const表示该成员函数并不修改成员变量,增加程序的可读性、可靠性
    */
    //查找是否存在给定值的结点
    bool Contains(AvlNode<T>* t, const T x) const;
    //中序遍历
    void InorderTraversal(AvlNode<T>* t);
    //前序遍历
    void PreorderTraversal(AvlNode<T>* t);
    //最小值结点
    AvlNode<T>* FindMin(AvlNode<T>* t) const;
    //最大值结点
    AvlNode<T>* FindMax(AvlNode<T>* t) const;
private:
    //求树的高度
    int GetHeight(AvlNode<T>* t);
    //右单旋转
    AvlNode<T>* LL(AvlNode<T>* t);
    //左单旋转
    AvlNode<T>* RR(AvlNode<T>* t);
    //先左后右双旋转
    AvlNode<T>* LR(AvlNode<T>* t);
    //先右后左双旋转
    AvlNode<T>* RL(AvlNode<T>* t);
};

//查找最大值结点
template <typename T>
AvlNode<T>* AvlTree<T>::FindMax(AvlNode<T>* t) const
{
    //空树
    if (t == nullptr)
        return nullptr;
    //由于是二叉排序树,结点的右子树为空,直接返回该结点
    if (t->right == nullptr)
        return t;
    //若不符合上面两种情况,继续向结点右子树深入
    return FindMax(t->right);
}

//查找最小值结点
template <typename T>
AvlNode<T>* AvlTree<T>::FindMin(AvlNode<T>* t) const
{
    //空树
    if (t == nullptr)
        return nullptr;
    //由于是二叉排序树,结点的左子树为空,直接返回该结点
    if (t->left == nullptr)
        return t;
    //若不符合上面两种情况,继续向结点的左子树深入
    return FindMin(t->left);
}

//获取树的高度
template <typename T>
int AvlTree<T>::GetHeight(AvlNode<T>* t)
{
    //空树
    if (t == nullptr)
        return -1;
    //非空树时,返回树的高度
    else
        return t->height;
}

//右单旋转
//结点左孩子的左子树插入结点导致的失衡
template <typename T>
AvlNode<T>* AvlTree<T>::LL(AvlNode<T>* t)
{
    AvlNode<T>* q = t->left;
    t->left = q->right;
    q->right = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

//左单旋转
//结点右孩子的右子树插入结点导致的失衡
template <typename T>
AvlNode<T>* AvlTree<T>::RR(AvlNode<T>* t)
{
    AvlNode<T>* q = t->right;
    t->right = q->left;
    q->left = t;
    t = q;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
    q->height = max(GetHeight(q->left), GetHeight(q->right)) + 1;
    return q;
}

//先左后右双向旋转
//结点的左孩子的右子树插入结点导致的失衡
template <typename T>
AvlNode<T>* AvlTree<T>::LR(AvlNode<T>* t)
{
    //双旋转可以通过两次单旋转实现
    //对结点的左结点进行RR旋转,再对结点进行LL旋转
    RR(t->left);
    return LL(t);
}

//先右后左双向旋转
//结点的右孩子的左子树插入结点
template <typename T>
AvlNode<T>* AvlTree<T>::RL(AvlNode<T>* t)
{
    //双旋转可以通过两次单旋转实现
    //对结点的右结点进行RR旋转,再对结点进行LL旋转
    LL(t->right);
    return RR(t);
}

//插入结点
template <typename T>
void AvlTree<T>::Insert(AvlNode<T>*& t, T x)
{
    if (t == nullptr)//情况一:结点为空
        t = new AvlNode<T>(x);
    else if (x < t->data)//情况二:插入数据小于结点
    {
        Insert(t->left, x);
        //判断平衡
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {
            // 分两种情况
            // 插入结点在结点的左孩子的左子树上  左左 LL
            // 插入结点在结点的左孩子的右子树上  左右 LR         
            if (x < t->left->data)//左左
                t = LL(t);
            else                  //左右
                t = LR(t);
        }
    }
    else if (x > t->data)//情况三:插入数据大于结点
    {
        Insert(t->right, x);
        //判断平衡
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            // 分两种情况
            // 插入结点在结点的右孩子的左子树上  左左 RL
            // 插入结点在结点的右孩子的右子树上  左右 RR  
            if (x > t->right->data)
                t = RR(t);
            else
                t = RL(t);
        }
    }
    else//情况四:插入数据在二叉树中有结点存在
        ;
    t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
}

// 删除结点
template <typename T>
bool AvlTree<T>::Delete(AvlNode<T>*& t, T x)
{
    //t为空 要删除的结点未找到  返回false
    if (t == nullptr)
        return false;
    //要删除的结点找到了
    else if (t->data == x)
    {
        //左右子树都非空
        if (t->left != nullptr && t->right != nullptr)
        {//在高度更大的那个子树上进行删除操作

            //左子树高度大,删除左子树中值最大的结点,将其赋给根结点
            if (GetHeight(t->left) > GetHeight(t->right))
            {
                t->data = FindMax(t->left)->data;
                Delete(t->left, t->data);
            }
            else//右子树高度更大,删除右子树中值最小的结点,将其赋给根结点
            {
                t->data = FindMin(t->right)->data;
                Delete(t->right, t->data);
            }
        }
        else
        {//左右子树有一个不为空,直接用需要删除的结点的子结点替换即可
            AvlNode<T>* old = t;
            t = t->left ? t->left : t->right;//t赋值为不空的子结点
            delete old;
        }
    }
    else if (x < t->data)//要删除的结点在左子树上
    {
        //递归删除左子树上的结点
        Delete(t->left, x);
        //判断是否仍然满足平衡条件
        if (GetHeight(t->right) - GetHeight(t->left) > 1)
        {
            if (GetHeight(t->right->left) > GetHeight(t->right->right))
            {
                //RL双旋转
                t = RL(t);
            }
            else
            {//RR单旋转
                t = RR(t);
            }
        }
        else//满足平衡条件 调整高度信息
        {
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }
    else//要删除的结点在右子树上
    {
        //递归删除右子树结点
        Delete(t->right, x);
        //判断平衡情况
        if (GetHeight(t->left) - GetHeight(t->right) > 1)
        {
            if (GetHeight(t->left->right) > GetHeight(t->left->left))
            {
                //LR双旋转
                t = LR(t);
            }
            else
            {
                //LL单旋转
                t = LL(t);
            }
        }
        else//满足平衡性 调整高度
        {
            t->height = max(GetHeight(t->left), GetHeight(t->right)) + 1;
        }
    }

   return true;
}

//查找结点
template <typename T>
bool AvlTree<T>::Contains(AvlNode<T>* t, const T x) const
{
    if (t == nullptr)
        return false;
    if (x < t->data)
        return Contains(t->left, x);
    else if (x > t->data)
        return Contains(t->right, x);
    else
        return true;
}

//中序遍历
template <typename T>
void AvlTree<T>::InorderTraversal(AvlNode<T>* t)
{
    if (t)
    {
        InorderTraversal(t->left);
        cout << t->data << ' ';
        InorderTraversal(t->right);
    }
}

//前序遍历
template <typename T>
void AvlTree<T>::PreorderTraversal(AvlNode<T>* t)
{
    if (t)
    {
        cout << t->data << ' ';
        PreorderTraversal(t->left);
        PreorderTraversal(t->right);
    }
}

int main()
{
    AvlTree<int> tree;
    int value;
    int tmp;

    cout << "请输入整数建立二叉树(-1结束):" << endl;
    while (cin >> value)
    {
        if (value == -1)
            break;
        tree.Insert(tree.root, value);
    }

    cout << "中序遍历:";
    tree.InorderTraversal(tree.root);

    cout << "\n前序遍历:";
    tree.PreorderTraversal(tree.root);

    cout << "\n请输入要查找的结点:";
    cin >> tmp;
    if (tree.Contains(tree.root, tmp))
        cout << "查找结点成功!" << endl;
    else
        cout << "值为" << tmp << "的结点不存在!" << endl;
    
    cout << "请输入要删除的结点:";
    cin >> tmp;
    tree.Delete(tree.root, tmp);
    
    cout << "删除后的中序遍历:";
    tree.InorderTraversal(tree.root);

    cout << "\n删除后的前序遍历:";
    tree.PreorderTraversal(tree.root);
    
}

测试结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值