C++_AVL树的概念及实现

AVL树的概念及特点

AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,AVL树是最先发明的自平衡二叉查找树。
二叉搜索树(二叉查找树,二叉排序树)或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 左、右子树也分别为二叉搜索树;
  4. 没有键值相等的节点。

AVL树是二叉搜索树的一种特殊情况,本质还是一颗二叉搜索树,只是AVL树的每个节点的两个子树的高度之差的绝对值(平衡因子)最多为1,也就是说,AVL树本质上是带了平衡功能的二叉搜索树,也被称为高度平衡树。如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(logN) ,搜索的时间复杂度为O(logN) 。

AVL树节点的定义

template <class T>
struct AVLNode
{
 AVLNode(const T& data)
  : _left(nullptr)
  , _right(nullptr)
  , _parent(nullptr)
  , _data(data)
  , _bf(0)
 {}
 
 AVLNode<T>* _left; // 该节点的左孩子
 AVLNode<T>* _right; // 该节点的右孩子
 AVLNode<T>* _parent; // 该节点的双亲
 T _data;
 int _bf; // 该节点的平衡因子
};

AVL树的插入

AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点;
  2. 调整节点的平衡因子。

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡。
根据节点插入位置的不同,AVL树的旋转分为四种:

  1. 新节点插入较高左子树的左侧—左左:右单旋
  2. 新节点插入较高右子树的右侧—右右:左单旋
  3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
  4. 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

总结:
假如以Parent为根的子树不平衡,即Parent的平衡因子为2或者-2,分以下情况考虑:

  1. Parent的平衡因子为2,说明Parent的右子树高,设Parent的右子树的根为SubR
    当SubR的平衡因子为1时,执行左单旋
    当SubR的平衡因子为-1时,执行右左双旋
  2. Parent的平衡因子为-2,说明Parent的左子树高,设Parent的左子树的根为SubL
    当SubL的平衡因子为-1时,执行右单旋
    当SubL的平衡因子为1时,执行左右双旋
    旋转完成后,原以Parent为根的子树高度降低,已经平衡,不需要再向上更新。

AVL树的代码实现

  • AVL树的结构
template <class T>
class AVLTree
{
public:
 typedef AVLNode<T> Node;
 typedef Node* pNode;
 
 bool Insert(const T& date);//插入
 void RotateRight(pNode cur); //右旋
 void RotateLeft(pNode cur);//左旋
 void _Inorder(pNode root);
 void Inorder();//中序遍历打印
 int Height(pNode root);
 bool _isBalance(pNode root);
 bool isBalance();//判断是否平衡
private:
 pNode _root = nullptr;
};
  • 插入
template<class T>
bool AVLTree<T>::Insert(const T& data)
{
 if (_root == nullptr)
 {
  _root = new Node(data);
  return true;
 }
 
 //按照二叉搜索树的性质找data在AVL中的插入位置
 pNode cur = _root;
 pNode parent = nullptr;
 while (cur)
 {
  parent = cur;
  if (data > cur->_data)
   cur = cur->_right;
  else if (data < cur->_data)
   cur = cur->_left;
  else
   return false;
 }
 
 //插入新节点
 cur = new Node(data);
 if (data > parent->_data)
  parent->_right = cur;
 else
  parent->_left = cur;
 cur->_parent = parent;
 
 //调节平衡因子
 while (parent)
 {
  // 更新双亲的平衡因子
  if (cur == parent->_left)
   parent->_bf--;
  else
   parent->_bf++;
  // 更新后检测双亲的平衡因子
  if (parent->_bf == 0)
   break;
  else if (parent->_bf == 1 || parent->_bf == -1)
  {
   // 插入前双亲的平衡因子是0,插入后双亲的平衡因子为1 或者 -1 ,说明以双亲为根的二叉树
   //的高度增加了一层,因此需要继续向上调整
   cur = parent;
   parent = parent->_parent;
  }
  else if (parent->_bf == 2 || parent->_bf == -2)
  // 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以parent为根的树进行旋转处理
  {
   //左边的左边高
   if (parent->_bf == -2 && cur->_bf == -1)
   {
    RotateRight(parent);
   }
   //右边的右边高
   else if (parent->_bf == 2 && cur->_bf == 1)
   {
    RotateLeft(parent);
   }
   //右边的左边高
   else if (parent->_bf == 2 && cur->_bf == -1)
   {
    pNode subR = parent->_right;
    pNode subRL = subR->_left;
    RotateRight(subR);
    RotateLeft(parent);
    if (subRL->_bf == 1)
    {
     subR->_bf = 0;
     parent->_bf = -1;
    }
    else if (subRL->_bf == -1)
    {
     subR->_bf = 1;
     parent->_bf = 0;
    }
   } 
   //左边的右边高(左右旋)
   else if (parent->_bf == -2 && cur->_bf == 1)
   {
    pNode subL = parent->_left;
    pNode subLR = subL->_right;
    RotateLeft(subL);
    RotateRight(parent);
    if (subLR->_bf == 1)       
    {
     parent->_bf = 0;
     subL->_bf = -1;
    }
    else if (subL->_bf == -1)
    {
     parent->_bf = 1;
     subL->_bf = 0;
    }
   }
   break;
  }
 }
 return true;
}
  • 左旋
template<class T>
void AVLTree<T>::RotateLeft(pNode cur)
{
 pNode subR = cur->_right;
 pNode subRL = subR->_left;
 subR->_left = cur;
 cur->_right = subRL;
 if (subRL)
  subRL->_parent = cur;
 if (cur != _root)
 {
  if (cur->_parent->_left == cur)
   cur->_parent->_left = subR;
  else
   cur->_parent->_right = subR;
  subR->_parent = cur->_parent;
 }
 else
 {
  _root = subR;
  subR->_parent = nullptr;
 }
 cur->_parent = subR;
 subR->_bf = cur->_bf = 0;   
}
  • 右旋
template<class T>
void AVLTree<T>::RotateRight(pNode cur)
{
 pNode subL = cur->_left;
 pNode subLR = subL->_right;
 subL->_right = cur;
 cur->_left = subLR;
 if (subLR) 
  subLR->_parent = cur;
 if (cur != _root)
 {
  if (cur->_parent->_left == cur)
  {
   cur->_parent->_left = subL;
  }
  else
  {
   cur->_parent->_right = subL;
  }
  subL->_parent = cur->_parent;
 }
 else
 {
  _root = subL;
  subL->_parent = nullptr;
 }
 cur->_parent = subL;
 subL->_bf = cur->_bf = 0;
}
  • 中序遍历打印
template<class T>
void AVLTree<T>::_Inorder(pNode root)
{
 if (root)
 {
  _Inorder(root->_left);
  cout << root->_data << " ";
  _Inorder(root->_right);
 }
}


template<class T>
void AVLTree<T>::Inorder()
{
 _Inorder(_root);
 cout << endl;
}
  • 检查是否平衡
template<class T>
int AVLTree<T>::Height(pNode root)
{
 if (root == nullptr)
  return 0;
 int Lh = Height(root->_left);
 int Rh = Height(root->_right);
 return Lh > Rh ? Lh + 1 : Rh + 1;
}


template<class T>
bool AVLTree<T>::_isBalance(pNode root)
{
 if (root == nullptr)
  return true;
 int Rh = Height(root->_right);
 int Lh = Height(root->_left);
 if (root->_bf != Rh - Lh)
 {
  cout << root->_data << "不平衡" << endl;
  cout << "平衡因子:" << root->_bf << "高度差:" << Rh - Lh << endl;
  return false;
 }
 return abs(root->_bf) < 2 && _isBalance(root->_left) && _isBalance(root->_right);
}


template<class T>
bool AVLTree<T>::isBalance()
{
 return _isBalance(_root);
}

AVL树的性能

  • AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即O(logN) 。
  • 如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。
  • 如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。
发布了71 篇原创文章 · 获赞 131 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43239560/article/details/99684697