C++之AVL树详解

首先我们需要了解一下AVL树概念:
       二叉树虽然可以提高搜索的效率,但是当数据有序接近有序二叉树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,因此便有人提出了AVL树的概念,当向二叉树插入新的节点后,如果可以保证每个节点的左右子树高度之差得到绝对值不超过1,即可降低树的高度,从而减少平均搜索长度

接下来我们就来简易搭建一下构架:

template<class K,class V>
struct AVLTreeNode
{
   AVLTreeNode<K,V>*_left;
   AVLTreeNode<K,V>*_right;
   AVLTreeNode<K,V>*_parent;
   pair<K,V> _kv;
   int _bf;
   AVLTreeNode(const pair<K,V>&kv) 
     :_left(nullptr)
     ,_right(nullptr)
     ,_parent(nullptr)
     ,_kv(kv)
     ,_bf(0)
  {}
}
template<class K,class V>
{
  typedef AVLTreeNodeM<K,V> Node;
public:
  bool Insert(const pair<K,V>& kv)
  {
    if(_root==nullptr)
    {
      _root=new Node(kv);
      return true;
    }
    Node* parent=nullptr;
    Node* cur=_root;
    while(cur)
    {
      if(cur->_kv.first<kv.first)
      {
         parent=cur;
         cur=cur->_right;
      }
      else if(cur->_kv.first>kv.first)
      {
         parent=cur;
         cur=cur->_left;
      }
      else//二者相同的情况
      {
         return false;
      }
    }
    cur=new Node(kv);
    if(parent->_kv.first<kv.first)
    {
       parent->_right=cur;
    }
    else if(parnet->kv.first>kv.first)
    { 
       parent->_left=cur;
    }
    cur->_parent=parent;
    return true;
  }
private:
  Node* _root=nullptr;
}

这里我们bf设置为0即可,接下来我么就来对平衡因子进行编写:

更新平衡因子的规则:
(1)新增在右,parent->bf++;新增在左,parent->bf--;

(2)更新后,parent->bf==1 or -1,说明parent之前是0,(注意:在这里parent之前一定不是2或者-2,否则之前就是不平衡的),说明左右子树高度相同,插入后有一边高,parent的高度变了,需要继续往上更新

(3)更新后,parent->bf==0,说明此时就不需要继续往上更新

(4)如果更新后,parent->bt==2或者-2,说明说明原来就是1或者-1,已经是平衡的临界值

template<class K,class V>
struct AVLTreeNode
{
   AVLTreeNode<K,V>*_left;
   AVLTreeNode<K,V>*_right;
   AVLTreeNode<K,V>*_parent;
   pair<K,V> _kv;
   int _bf;
   AVLTreeNode(const pair<K,V>&kv) 
     :_left(nullptr)
     ,_right(nullptr)
     ,_parent(nullptr)
     ,_kv(kv)
     ,_bf(0)
  {}
}
template<class K,class V>
{
  typedef AVLTreeNodeM<K,V> Node;
public:
  bool Insert(const pair<K,V>& kv)
  {
    if(_root==nullptr)
    {
      _root=new Node(kv);
      return true;
    }
    Node* parent=nullptr;
    Node* cur=_root;
    while(cur)
    {
      if(cur->_kv.first<kv.first)
      {
         parent=cur;
         cur=cur->_right;
      }
      else if(cur->_kv.first>kv.first)
      {
         parent=cur;
         cur=cur->_left;
      }
      else//二者相同的情况
      {
         return false;
      }
    }
    cur=new Node(kv);
    if(parent->_kv.first<kv.first)
    {
       parent->_right=cur;
    }
    else if(parnet->kv.first>kv.first)
    { 
       parent->_left=cur;
    }
    cur->_parent=parent;
    //平衡因子编写
    while(parent)
    {
     if(cut==parent->_right)
     {
       parent->_bf++;
     }
     else
     {
       parent->_bf--;
     }
     if(parent->_bf==0)
     {
       break;
     }
     if(abs(parent>_bf)==1)
     {
       parent=parent->_parent;
       cur=cur->_parent;
     }
     else if(abs(parent->_bf)==2)
     {
       //说明parent的子树已经不平衡了,需要旋转处理
     }
     else
     {
       assert(false);//理论上是不会到这里的
     }
    }
    return true;
  }
private:
  Node* _root=nullptr;
}

 接下来我们一起来了解一下旋转机制:
首先原则就是:

(1)旋转为平衡树

(2)保持搜索树原则

接下来我们就逐个讲解一下各个旋转规则:、


一.左单旋:
 

首先我们要知道为什么要叫做左旋转,其实根本就是右边高了,导致平衡因子变大,具体图如下:

 最右边的子树加1,此时60的平衡因子就变作1,30就变作2,此时就需要旋转。具体调节方法如上图;

其实也就是只要最右边加成员,就会发生左旋:

一.左旋

template<class K,class V>
struct AVLTreeNode
{
   AVLTreeNode<K,V>*_left;
   AVLTreeNode<K,V>*_right;
   AVLTreeNode<K,V>*_parent;
   pair<K,V> _kv;
   int _bf;
   AVLTreeNode(const pair<K,V>&kv) 
     :_left(nullptr)
     ,_right(nullptr)
     ,_parent(nullptr)
     ,_kv(kv)
     ,_bf(0)
  {}
}
template<class K,class V>
{
  typedef AVLTreeNodeM<K,V> Node;
public:
  bool Insert(const pair<K,V>& kv)
  {
    if(_root==nullptr)
    {
      _root=new Node(kv);
      return true;
    }
    Node* parent=nullptr;
    Node* cur=_root;
    while(cur)
    {
      if(cur->_kv.first<kv.first)
      {
         parent=cur;
         cur=cur->_right;
      }
      else if(cur->_kv.first>kv.first)
      {
         parent=cur;
         cur=cur->_left;
      }
      else//二者相同的情况
      {
         return false;
      }
    }
    cur=new Node(kv);
    if(parent->_kv.first<kv.first)
    {
       parent->_right=cur;
    }
    else if(parnet->kv.first>kv.first)
    { 
       parent->_left=cur;
    }
    cur->_parent=parent;
    //平衡因子编写
    while(parent)
    {
     if(cut==parent->_right)
     {
       parent->_bf++;
     }
     else
     {
       parent->_bf--;
     }
     if(parent->_bf==0)
     {
       break;
     }
     if(abs(parent>_bf)==1)
     {
       parent=parent->_parent;
       cur=cur->_parent;
     }
     else if(abs(parent->_bf)==2)
     {
       //说明parent的子树已经不平衡了,需要旋转处理
       if(parent->_bf==2 &&cur->bf==0)
       { 
         Rotatel(parent);
       }
       break;
     }
     else
     {
       assert(false);//理论上是不会到这里的
     }
    }
    return true;
  }
private:
  void Rotatel(Node* parent)
  {
    Node* subR=parent->_right;
    Node* suRL=subR->_left;
    parent->_right=subRL;
    if(subPL)
    {
      subRL->_parent=parent;
    }
    Node* ppNode=parent->_parent;
    subR->_left=parent;
    parent->_parent=subR;
    if(parent==_root)//如果父母就是根
    {
      _root=subR;
      subR->_parent=nullptr;
    }
    else//如果父母不是根,而是子树
    {
      if(ppNode->_left=parent)
      {
        ppNode->_left=subR;
      }
      else
      {
        ppNode->_right=subR;
      }
      subR->_parent=ppNode;
    }
    subR->_bf=parent->_bf=0;
  }
private:
  Node* _root=nullptr;
}

二.右旋

具体图如下: 

void RotateR(Node* parent)
{
    Node* subL=parent->_left;
    Node* subLR=subL->_right;
    parent->_left=subLR;
    if(subLR)
    {
      subLR->_parent=parent;
    }
    Node* ppNode=parent->_parent;
    subL->_right=parent;
    parent->_parent=subL;
    if(_root==parent)
    {
      _root=subL;
      subL->_parent=nullptr;
    }
    else
    {
      if(ppNode->_left==_parent)
      {
         ppNode->_left=subL;
      }
      else
      {
         ppNode->_right=subL;
      }
      subL->_parent=ppNode;
    }
    subL->_bf=parent->_bf=0;
}

前提条件如下:

if((parent->_bf==-2) && (cur->_bf==-1))
{
   RotateR(parent);
}

三.双旋

(1)左右双旋

 

void RotateLR(Node* parent)
{
   Node* subL=parent->_left;
   Node* subLR=subL->_right;
   int bf=subLR->_bf;
   RotateL(parent->_left);
   RotateR(parent);
   subLR->_bf=0;
   if(bf==1)
   { 
     parent->_bf=0;
     subL->_bf=-1;
   }
   else if(bf==-1)
   {
     parent->_bf=1;
     subL->_bf=0;
   }
   else if(bf==0)
   {
     parent->_bf=0;
     subL->_bf=0;
   }
   else
   {
     assert(false);
   }
}

(2)右左双旋

void RotateRL(Node* parent)
{
   Node* subR=parent->_right;
   Node* subRL=subR->_left;
   int bf=subRL->_bf;
   RotateR(parent->_left);
   RotateL(parent);
   if(bf==1)
   {
     subR->_bf=0;
     parent->_bf=-1; 
     subRL->_bf=0;
   }
   else if(bf==-1)
   {
     subR->_bf=1;
     parent->_bf=0;
     subRL->_bf=0;
   }
   else if(bf==0)
   {
     parent->_bf=0;
     subR->_bf=0;
   }
   else 
   {
     assert(false);
   }
}

最后我们来了解一下旋转的价值与意义:
1.使当前这棵树平衡

2.降低树的高度

以上就是文章所有内容,希望对您有帮助!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

抱着太阳吃雪糕z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值