AVL树详解以及实现

1、AVL树定义

AVL树是一种高度平衡的二叉搜索树,它既能保持二叉树的高度平衡,又尽量降低了二叉树的高度,这样减少了树的平均搜索长度,达到了很好的搜索效率的同时也保证了不错的插入和删除效率

其必须满足如下约束条件:

  1. 每个节点的左子树和右子树的高度之差的绝对值都不超过1
  2. 树中的每个节点的左子树和右子树都是AVL树
  3. 满足正常的二叉查找树的规则(每个节点左边节点的key大于右边的key

AVL树效率:
一棵有n个节点的AVL树,其高度可以保持在O(log(n))插入/删除/查找的时间复杂度在最坏和平均情况下都是O(log(n))

一颗正常的AVL树:
这里写图片描述

AVL树一般我们最常用的操作就是那么几个:插入、删除、查询

要想维持住查找的平均时间复杂度为O(log(n)),我们就必须不断的在删除和插入元素的时候通过一次或多次树旋转来重新平衡这个树(达到树的深度为O(log(n))

1.1、AVL树的节点

首先我们先定义一下AVL树的节点:

//AVL树的节点
template<typename K, typename T>
struct node {
    //数据
    K key;
    T data;
    int _bf;     //平衡因子,每个节点的平衡因子等于右子树的高度减去左子树的高度


    node* parent;//父节点

                 //左右节点
    node* left;
    node* right;

    node(const K& k, const T& d) :data(d), key(k), left(nullptr), right(nullptr), parent(nullptr), _bf(0) {}

};
AVL树要求左右子树高度差的绝对值不超过1,因此AVL树必须通过旋转调节平衡因子的方式去保持平衡。

2、插入

2.1、AVL树插入时的四个状态

PS:假设X为处于不平衡状态的树的根节点

1. 插入点位于X的左子节点的左子树 —— 左左

2. 插入点位于X的左子节点的右子树 —— 左右

3. 插入点位于X的右子节点的右子树 —— 右右

4. 插入点位于X的右子节点的左子树 —— 右左

2.2、单旋(情况1、3)

1、3的情况是一样的,所以我们可以当作同一种情况来讨论,如图:
这里写图片描述

以上就是左左的情况(插入点位于X的左子节点的左子树),可以看到当节点X为根节点,在插入1的时候,出现了不平衡的状态,一般这种处于外侧的节点插入导致的不平衡我们都采用单旋的方式解决,如下:
这里写图片描述

一般通过提拉X的子树到X的位置的方式去达到重新平衡的功能 —— 左旋或者右旋

左左和右右的情况类似,解决方法也是一样的

代码实现如下(只贴了左旋的代码):


template<typename K, typename T>
void AVL_tree<K, T>::SingleRotateL(node * t)
{
    node* parent = t->parent;
    node* rightNode = t->right;
    node* right_leftSonNode = rightNode->left; //右节点的左儿子
    t->right = right_leftSonNode;
    if (right_leftSonNode != nullptr) {        //如果右节点存在左子树

        right_leftSonNode->parent = t;
    }
    rightNode->left = t;
    rightNode->parent = parent;
    t->parent = rightNode;
    if (parent == nullptr) { //如果当前t没有父节点
        rightNode->parent = nullptr;
        _root = rightNode;
    }
    else if (parent->left == t) {
        parent->left = rightNode;
    }
    else {
        parent->right = rightNode;
    }


    t->_bf = rightNode->_bf = 0;//调节平衡因子
}

2.3、双旋转(情况2、4)

2、4的情况也可以归于同一类,我们首先先看一个满足情况2的AVL树(插入点位于X的左子节点的右子树):

这里写图片描述
可以看到,插入点K位于X的左节点的右子树上,刚好满足我所说的左右情况,如何解决呢?用两次单旋的方式去解决:
这里写图片描述

具体的细节就不再赘述了,同样也是两次的提拉操作,达到了新的平很状态。
实现代码如下(情况二):


template<typename K, typename T>
void  AVL_tree<K, T>::DoubleRotateLR(node * t)
{
    node* leftNode = t->left;
    node* left_rightSonNode = leftNode->right;  //左节点的右儿子

    int bf = left_rightSonNode->_bf;

    SingleRotateL(leftNode);
    SingleRotateR(t);
    if (bf == 1) {                              //左节点的右儿子存在右子节点
        leftNode->_bf = -1;
        t->_bf = 0;
    }
    else if (bf == -1) {                        //左节点的右儿子存在左子节点
        leftNode->_bf = 0;
        t->_bf = 1;
    }
    else {
        leftNode->_bf = 0;
        t->_bf = 0;
    }
    left_rightSonNode->_bf = 0;
}

3、查找

查找的情况就很简单了,其状态如下:

1、若搜索树为空则返回空;

2、若要查找的节点等于根节点,返回根节点;

3、若要查找的节点大于根节点,递归查找右子树,反之递归查找左子树。

实现:

template<typename K, typename T>
bool AVL_tree<K, T>::Find(const K & key)
{
    if (Root() == nullptr)return false;

    node* cur = Root();
    while (cur != nullptr) {
        if (key == cur->key)return true;
        else if (key < cur->key) {
            cur = cur->left;
        }
        else {
            cur = cur->right;
        }
    }
    return false;
}

完整代码

#pragma once

#include<iostream>

template<typename K, typename T>
class AVL_tree {

    //AVL树的节点
    struct node {
        //数据
        K key;
        T data;
        int _bf;     //平衡因子,每个节点的平衡因子等于右子树的高度减去左子树的高度


        node* parent;//父节点

        //左右节点
        node* left;
        node* right;

        node(const K& k, const T& d) :data(d), key(k), left(nullptr), right(nullptr), parent(nullptr), _bf(0) {}

    };

public:
    typedef node node_type;
private:
    //根节点
    node* _root;

private:
    //内部自用函数
    void SingleRotateL(node* t);
    void DoubleRotateRL(node* t);
    void DoubleRotateLR(node* t);
    void SingleRotateR(node* t);

    void _MidOrderPrint(node * root);
public:
    void MidOrderPrint();                     //按排序顺序输出
    node* Root() { return _root; }

    bool Insert(const K& key, const T& value);//插入节点


    //void Remove(const K& value);            //删除节点


    bool Find(const K& key);                  //查找节点
    AVL_tree() :_root(nullptr) {}

};





template<typename K, typename T>
void AVL_tree<K, T>::DoubleRotateRL(node * t)
{
    node* rightNode = t->left;
    node* right_leftSonNode = rightNode->right;   //右节点的左儿子

    int bf = right_leftSonNode->_bf;

    SingleRotateR(rightNode);
    SingleRotateL(t);
    if (bf == 1) {                                //右节点的左儿子存在左子节点
        rightNode->_bf = 0;
        t->_bf = -1;
    }
    else if (bf == -1) {                          //左节点的左儿子存在右子节点
        rightNode->_bf = 1;
        t->_bf = 0;
    }
    else {
        rightNode->_bf = 0;
        t->_bf = 0;
    }
    right_leftSonNode->_bf = 0;
}

template<typename K, typename T>
void  AVL_tree<K, T>::DoubleRotateLR(node * t)
{
    node* leftNode = t->left;
    node* left_rightSonNode = leftNode->right;  //左节点的右儿子

    int bf = left_rightSonNode->_bf;

    SingleRotateL(leftNode);
    SingleRotateR(t);
    if (bf == 1) {                              //左节点的右儿子存在右子节点
        leftNode->_bf = -1;
        t->_bf = 0;
    }
    else if (bf == -1) {                        //左节点的右儿子存在左子节点
        leftNode->_bf = 0;
        t->_bf = 1;
    }
    else {
        leftNode->_bf = 0;
        t->_bf = 0;
    }
    left_rightSonNode->_bf = 0;
}

template<typename K, typename T>
void AVL_tree<K, T>::SingleRotateR(node * t) {
    node* parent = t->parent;
    node* leftNode = t->left;
    node* left_rightSonNode = leftNode->right; //左节点的右儿子
    t->left = left_rightSonNode;
    if (left_rightSonNode != nullptr) {        //如果左节点存在右子树

        left_rightSonNode->parent = t;
    }
    leftNode->right = t;
    leftNode->parent = parent;
    t->parent = leftNode;
    if (parent == nullptr) { //如果当前t没有父节点
        leftNode->parent = nullptr;
        _root = leftNode;
    }
    else if (parent->left == t) {
        parent->left = leftNode;
    }
    else {
        parent->right = leftNode;
    }


    t->_bf = leftNode->_bf = 0;//调节平衡因子
}

template<typename K, typename T>
inline void AVL_tree<K, T>::_MidOrderPrint(node * root)
{
    if (Root() == nullptr) {
        return;
    }
    MidOrderPrint(root->left);
    cout << root->key << endl;
    MidOrderPrint(root->right);
}

template<typename K, typename T>
void AVL_tree<K, T>::SingleRotateL(node * t)
{
    node* parent = t->parent;
    node* rightNode = t->right;
    node* right_leftSonNode = rightNode->left; //右节点的左儿子
    t->right = right_leftSonNode;
    if (right_leftSonNode != nullptr) {        //如果右节点存在左子树

        right_leftSonNode->parent = t;
    }
    rightNode->left = t;
    rightNode->parent = parent;
    t->parent = rightNode;
    if (parent == nullptr) { //如果当前t没有父节点
        rightNode->parent = nullptr;
        _root = rightNode;
    }
    else if (parent->left == t) {
        parent->left = rightNode;
    }
    else {
        parent->right = rightNode;
    }


    t->_bf = rightNode->_bf = 0;//调节平衡因子
}
template<typename K, typename T>
bool AVL_tree<K, T>::Insert(const K& key, const T& value)
{
    if (_root == nullptr) {//树为空
        _root = new node(key, value);
        return true;
    }
    node* parent = nullptr;
    node* cur = Root();
    while (cur) {
        if (key < cur->key) {
            parent = cur;
            cur = cur->left;
        }
        else if (key > cur->key) {
            parent = cur;
            cur = cur->right;
        }
        else {
            return false;
        }
    }
    cur = new node(key, value);
    if (key > parent->key) {
        parent->right = cur;
        cur->parent = parent;
    }
    else {
        parent->left = cur;
        cur->parent = parent;
    }
    //旋转和调节
    while (parent) {//判断当前状态,并层层调节平衡因子
        if (parent->left == cur)
        {
            parent->_bf--;
        }
        else
        {
            parent->_bf++;
        }

        if (parent->_bf == 0)  //已经是平衡树  
        {
            break;
        }
        else if (parent->_bf == -1 || parent->_bf == 1) //继续回溯  
        {
            cur = parent;
            parent = cur->parent;
        }
        else {                                     //_bf== -2 || 2 ,进入调整阶段 

            if (parent->_bf == -2) {               //在当前树的左方,左左或左右
                if (cur->_bf == -1) {              //左左,插入点位于当前树的左节点的左子树,进行右单旋
                    SingleRotateR(parent);
                }

                else {                             //左右,插入点位于当前树的左节点的右子树,进行双旋(先进行一次左旋,在进行一次右旋)
                    DoubleRotateLR(parent);
                }
            }
            else {                                 //在当前树的右方,右左或者右右
                if (cur->_bf == 1) {               //右右,插入点位于当前树的右节点的右子树,进行左单旋
                    SingleRotateL(parent);
                }
                else {                             //右左,插入点位于当前树的右节点的左子树,进行双旋(先进行一次右旋,在进行一次左旋)
                    DoubleRotateRL(parent);
                }
            }
            break;                                 //结束回溯
        }

    }
    return true;
}

template<typename K, typename T>
bool AVL_tree<K, T>::Find(const K & key)
{
    if (Root() == nullptr)return false;

    node* cur = Root();
    while (cur != nullptr) {
        if (key == cur->key)return true;
        else if (key < cur->key) {
            cur = cur->left;
        }
        else {
            cur = cur->right;
        }
    }
    return false;
}
template<typename K, typename T>
inline void AVL_tree<K, T>::MidOrderPrint()
{
    _MidOrderPrint(Root());
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值