【C++模拟实现】手撕AVL树

【C++模拟实现】手撕AVL树

目录

作者:爱写代码的刚子

时间:2023.9.10

前言:本篇博客将会介绍AVL树的模拟实现(模拟AVL树的插入),以及如何去验证是否为AVL树

AVL树的介绍(百度百科)

AVL树本质上还是一棵二叉搜索树,它的特点是:

  1. 本身首先是一棵二叉搜索树。

  2. 带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。

AVL树insert函数的实现代码

cpp 复制代码
template<class K,class V>
class AVLTreeNode
{
public:
    AVLTreeNode(const pair<K,V>& kv)
    :_left(nullptr)
    ,_right(nullptr)
    ,_parent(nullptr)
    ,_kv(kv)
    ,_bf(0)
    {}
    
    AVLTreeNode* _left;
    AVLTreeNode* _right;
    AVLTreeNode* _parent;//需要设立父节点指针
    pair<K,V> _kv;

    int _bf;
};

template<class K,class V>
class AVLTree
{
    typedef AVLTreeNode<K,V> Node;
public:
    AVLTree()
    :_root(nullptr)
    {}

    
    bool insert(const pair<K,V>& kv)
    {
        if(_root==nullptr)
        {
            _root=new Node(kv);
            return true;
        }
        else
        {
            Node* cur=_root;
            Node* parent=nullptr;//设计parent指针是必要的
            while(cur)
            {
                if(cur->_kv.first>kv.first)
                {
                    parent=cur;
                    cur=cur->_left;
                }
                else if(cur->_kv.first<kv.first)
                {
                    parent=cur;
                    cur=cur->_right;
                }
                else{
                    return false;
                }
            }
            cur=new Node(kv);
          
          //判断新加入的节点是父节点的左子树还是右子树
            if(parent->_kv.first>kv.first)
            {
                parent->_left=cur;
            }
            else{
                parent->_right=cur;
            }
            cur->_parent=parent;
            
            while(parent)
            {
              //及时调整父节点的平衡因子
                if(parent->_left==cur)
                {
                    --parent->_bf;
                }
                else{
                    ++parent->_bf;
                }

                if(parent->_bf==0)//当父节点的平衡因子为0时停止调整
                {
                    break;
                }
                else if(parent->_bf==-1||parent->_bf==1)
                {
                    cur=parent;
                    parent=parent->_parent;
                }
                else if(parent->_bf==2||parent->_bf==-2)//处理异常情况
                {
                    //出现问题的情况
                    if(parent->_bf==-2&&cur->_bf==-1)
                    {
                        _RotateR(parent);//右单旋
                    }
                    else if(parent->_bf==2&&cur->_bf==1)
                    {
                        _RotateL(parent);//左单旋
                    }
                    else if(parent->_bf==-2&&cur->_bf==1)
                    {   
                        _RotateLR(parent);//左右双旋

                    }
                    else if(parent->_bf==2&&cur->_bf==-1)
                    {
                        _RotateRL(parent);//右左双旋
                    }
                    else
                    {
                        assert(false);
                    }


                    break;

                }
                else{
                    assert(false);
                }
                
            }
        }
        return true;
    } 
    

    void _RotateR(Node* parent)//右单旋的实现
    {
        Node*cur=parent->_left;
        Node*curRight=cur->_right;
        Node*ppnode=parent->_parent;
        
        
        cur->_right=parent;
        parent->_left=curRight;

        if(curRight)//curRight可能是nullptr
        {
            curRight->_parent=parent;
        }
        parent->_parent=cur;
        
        //处理ppnode
        if(parent==_root)//parent为头节点时需要单独处理
        {
            _root=cur;
            cur->_parent=nullptr;

        }
        else
        {
            if(ppnode->_left==parent)
            {
                ppnode->_left=cur;
            }
            else{
                ppnode->_right=cur;
            }
            cur->_parent=ppnode;
        }
        parent->_bf=cur->_bf=0;

    }
    void _RotateL(Node* parent)
    {
        Node* cur=parent->_right;
        Node* curLeft=cur->_left;
        Node* ppnode=parent->_parent;

        cur->_left=parent;
        parent->_right=curLeft;

        if(curLeft)
        {
            curLeft->_parent=cur;
        }
        parent->_parent=cur;

        if(parent==_root)
        {
            _root=cur;
            cur->_parent=nullptr;
        }
        else
        {
            if(ppnode->_left==parent)
            {
                ppnode->_left=cur;
            }
            else{
                ppnode->_right=cur;
            }
            cur->_parent=ppnode;
        }
        parent->_bf=cur->_bf=0;
    }
    void _RotateLR(Node* parent)
    {
        Node* cur=parent->_left;
        Node* curRight=cur->_right;
        int bf=curRight->_bf;

        _RotateL(cur);
        _RotateR(parent);

        //最好再处理一下平衡因子,减少耦合度
        if(bf==0)//单链情况下
        {
            parent->_bf=0;
            cur->_bf=0;
            curRight->_bf=0;
        }
        else if(bf==-1)
        {
            parent->_bf=1;
            curRight->_bf=0;
            cur->_bf=0;
        }
        else if(bf==1)
        {
            parent->_bf=-1;
            curRight->_bf=0;
            cur->_bf=0;
        }
        else{
            assert(false);
        }
    }
    void _RotateRL(Node* parent)
    {
        Node* cur=parent->_right;
        Node* curLeft=cur->_left;
        int bf=curLeft->_bf;

        _RotateR(cur);
        _RotateL(parent);

        if(bf==0)
        {
            parent->_bf=0;
            curLeft->_bf=0;
            cur->_bf=0;
        }
        else if(bf==1)
        {
            parent->_bf=0;
            curLeft->_bf=-1;
            cur->_bf=0;
        }
        else if(bf==-1)
        {
            parent->_bf=0;
            curLeft->_bf=1;
            cur->_bf=0;
        }
        else{
            assert(false);
        }
        
    }
  private:
    


    Node* _root;
};

验证是否为AVL树

cpp 复制代码
int _Height(Node* root)
    {
        if(root==nullptr)
        {
            return 0;
        }
        int leftHeight=_Height(root->_left);
        int rightHeight=_Height(root->_right);

        return leftHeight>rightHeight?leftHeight+1:rightHeight+1;
    }
    

    bool _Isbalance()
    {
        return _Isbalance(_root);
    }
    bool _Isbalance(Node* root)
    {
        if(root==nullptr)
        {
            return true;
        }

        int right=_Height(root->_right);
        int left=_Height(root->_left);

        if(root->_bf!=right-left)
        {
            cout<<"平衡因子异常"<<root->_bf<<" "<<right<<" "<<left<<endl;
            return false;
        }

        return abs(right-left)<2&&_Isbalance(root->_left)&&_Isbalance(root->_right);


    }
  • 根据AVL树的特性引入两个成员函数_Height函数用于计算二叉树的高度

  • 以下为验证结果:

AVL树模拟实现的要点

易忘点

一定要时刻注意_parent指针的修改!尤其旋转函数中需要判断旋转后的二叉树的根节点是否还有父亲节点,如果有,需要在旋转前先保存,之后再链接上。

AVL树的旋转思路

  1. 新增在左,parent平衡因子减减
  2. 新增在右,parent平衡因子加加
  3. 更新后parent平衡因子 == 0,说明parent所在的子树的高度不变,不会再影响祖先,不用再继续沿着到eot的路径往上更新
  4. 更新后parent平衡因子 == 1 0r -1,说明parent所在的子树的高度变化,会再影响祖先,需要继续沿着到root的路径往上更新更新后
  5. 更新后parent平衡因子 == 2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让他平衡
  6. 更到根节点,插入结束

由于AVL树画图较为麻烦,作者先不画了,可以看看其他大佬的博客,一些需要注意的地方已经写在代码注释里了,AVL树的删除之后有机会可以模拟实现一下。

AVL树的调试较为麻烦,模拟实现可以提高自己的调试能力。

相关推荐
杨荧10 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的服装商城系统学科竞赛管理系统
java·开发语言·vue.js·spring boot·spring cloud·java-ee·kafka
白子寰16 分钟前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_0121 分钟前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
XuanRanDev25 分钟前
【每日一题】LeetCode - 三数之和
数据结构·算法·leetcode·1024程序员节
gkdpjj27 分钟前
C++优选算法十 哈希表
c++·算法·散列表
代码猪猪傻瓜coding27 分钟前
力扣1 两数之和
数据结构·算法·leetcode
王俊山IT28 分钟前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
为将者,自当识天晓地。30 分钟前
c++多线程
java·开发语言
-Even-31 分钟前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
小政爱学习!32 分钟前
封装axios、环境变量、api解耦、解决跨域、全局组件注入
开发语言·前端·javascript