C++ -- AVL树插入实现和测试

简介: 1. AVL树概念AVL树就是自平衡二叉查找树,为了解决二叉树退化为单链表,使得增删查改时间度为O(N),这里采用控制平衡策略达到效率是O(logN)。2. AVL树满足性质每个节点的左右子树高度之差绝对不能超过1左边插入(父节点高度差-1)右边插入(父节点高度差+1)不插入(自身节点高度差为0)

1. AVL树概念

AVL树就是自平衡二叉查找树,为了解决二叉树退化为单链表,使得增删查改时间度为O(N),这里采用控制平衡策略达到效率是O(logN)。

2. AVL树满足性质

每个节点的左右子树高度之差绝对不能超过1

  • 左边插入(父节点高度差-1)
  • 右边插入(父节点高度差+1)
  • 不插入(自身节点高度差为0

3. AVL节点结构

template <class KEY, class VAULE>
struct AVLtree_node
{
  AVLtree_node<KEY, VAULE>* _left; //左节点指向
  AVLtree_node<KEY, VAULE>* _right; //右节点指向
  AVLtree_node<KEY, VAULE>* _parent; //父节点指向
  pair<KEY, VAULE> _couple; //存储key/value
  int _balanceFactor; //平衡因子(左右子树高度差)
  AVLtree_node(const pair<KEY, VAULE>& couple)
    :_left(nullptr)
    :_right(nullptr)
    :_parent(nullptr)
    :_couple(couple)
    :_balanceFactor(0)
  {}
};

4. 插入操作

插入阶段

根节点为空

new节点,改变根指向,返回true

根节点不为空

找到插入位置

右查找:当前节点key值 < 插入节点key值

左查找:当前节点key值 > 插入节点key值

当前节点key值 = 插入节点key值 :直接返回false

在对应待插入位置插入

new节点,当前插入位置指向该节点

右插入:当前节点key值 < 插入节点key值

左插入: 当前节点key值 > 插入节点key值

当前被插入节点父指针指向指向被连接节点

自动平衡(TODO)

bool insert(const pair<KEY, VAULE>& couple)
{
    //二叉搜索树阶段
  if (_root == nullptr) //根为空:直接new并指向返回
  {
    _root = new node(couple);
    return true;
  }
  /*找插入位置*/
  node* parent = nullptr; //起初根节点的父节点为nullptr
  node* cur = _root; //被插入节点指向
  while (cur)
  {
    if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
    {
      parent = cur;
      cur = cur->_right;
    }
    else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
    {
      parent = cur;
      cur = cur->_left;
    }
    else //当前节点key值 = 插入节点key值:直接退出
    {
      return false;
    }
  }
  /*在对应位置插入*/
  cur = new node(couple);
  if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
  {
    parent->_left = newnode;
  }
  else //右插入:当前节点key值 < 插入节点key值
  {
    parent->_right = newnode;
  }
  cur->_parent = parent; //反向链接
  /*自动平衡 TODO*/
    //AVL树阶段
}
  1. 平衡阶段铺垫

注意:并没有画上父节点指向,为了便于观察所以没有画,默认每个节点是有父节点指向的

  • 右边插入构图
  • 微信图片_20230523234451.png
  • 左边插入构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGLjZnpU-1684114665514)(https://typora130.oss-cn-nanjing.aliyuncs.com/QQ截图20230513154519.png)]


上面左右插入构图示范中,观察发现该节点左右子树高度差超过了1就会形成子树单链表,这样就会影响到效率,那么这里的解决办法就是对其旋转处理(待解),另外观察到上面错误高度差情况,会发现一旦插入其被插入节点的部分祖先的平衡因子(高度差)可能改变,所以,插入新增节点会影响部分或全部祖先的平衡因子,平衡因子怎么来更新呢?如果新增节点在其父节点左边,父节点平衡因子-1,如果新增节点在其父节点右边,父节点平衡因子+1,每个节点的平衡因子初始状态都是0。是什么时候平衡因子更新?当插入节点的父节点的平衡因子变化(不为0),那么就需要对其被插入节点的部分或者全部祖先的平衡因子更新。


观察:右边插入三种情况示范:

微信图片_20230523234630.png

微信图片_20230523234709.png微信图片_20230523234647.png

被插入节点父节点平衡高度不为0,就向上更新祖先的平衡因子,向上更新的依据是该节点左右子树高度变化了;如果插入节点父节点平衡高度为0,就无需向上更新平衡因子

后面两种情况观察:当祖先节点的平衡因子为2或者-2时,就不应该再往上层更新平衡因子了(key值为7的节点的平衡因子就没必要更新了),因为此时只要有一个祖先的平衡因子为2说明就已经有问题了,就需要对这部分子树来调整达到一种平衡状态(旋转处理)。


自动平衡

更新平衡因子

改变被插入节点父节点平衡因子

被插入节点在其父节点右子树中:父节点平衡因子+1

被插入节点在其父节点左子树中:父节点平衡因子-1

判断是否继续向上更新平衡因子

父节点平衡因子为1或者-1,继续更新

父节点平衡因子为0,无需更新

父节点平衡因子为2或者-2,子树不平衡,旋转处理使得平衡(TODO)

bool insert(const pair<KEY, VAULE>& couple)
{
  if (_root == nullptr) //根为空:直接new并指向返回
  {
    _root = new node(couple);
    return true;
  }
  /*找插入位置*/
  node* parent = nullptr; //起初根节点的父节点为nullptr
  node* cur = _root; //被插入节点指向
  while (cur)
  {
    if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
    {
      parent = cur;
      cur = cur->_right;
    }
    else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
    {
      parent = cur;
      cur = cur->_left;
    }
    else //当前节点key值 = 插入节点key值:直接退出
    {
      return false;
    }
  }
  /*在对应位置插入*/
  cur = new node(couple);
  if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
  {
    parent->_left = newnode;
  }
  else //右插入:当前节点key值 < 插入节点key值
  {
    parent->_right = newnode;
  }
  cur->_parent = parent; //反向链接
  /*自动平衡 TODO*/
  //更新平衡因子
  while (parent)
  {
    if (cur == parent->_right) //被插入节点在其父节点右子树中
    {
      parent->_balanceFactor++;
    }
    else //被插入节点在其父节点左子树中
    {
      parent->_balanceFactor--;
    }
    if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) //继续更新平衡因子
    {
      parent = parent->_parent;
      cur = cur->_parent;
    }
    else if (parent->_balanceFactor == 0) //无需更新
    {
      break;
    }
    else if (parent->_balanceFactor == 2 || parent->_balanceFactor == -2) //子树不平衡(旋转处理)
    {
      /*TODO*/
            break; //旋转之后达到平衡无需更新平衡因子(操作完后便知)
    }
    else
    {
      assert(false);
    }
  }
}
  1. 平衡阶段旋转处理

旋转目的:让子树平衡并且降低子树高度

  • 左单旋
  • 左单旋一定是右高左低

三个举例:

微信图片_20230523235024.png

微信图片_20230523235048.png

微信图片_20230523235105.png

三个举例来抽象得到宏观图:


微信图片_20230523235201.png

H为这颗子树高度,当H为0,反应就是上述第一个例子;当H为1,反应就是上述第二个例子;当H为2,反应就是上述第三个例子。当H为x时,其调整方法不变。当H为2时,此时有三种情况的高度为2的树:

微信图片_20230523235225.png

但是这里c这棵子树必须是x类型的高度为2的树,a,b这两课子树可以是任意一种都可以。为什么c子树必须是x类型的树呢?如果是y型可能插入不会引发旋转,也可能只会局部旋转:

微信图片_20230523235302.png如何旋转?

微信图片_20230523235344.png

  1. 让cur的左子树(curLeft)放在parent的右子树位置并且curLeft的父节点指向指向parent
  2. 让parent放在cur的左子树位置并且parent的父节点指向指向cur
  3. cur变为子树或者整颗树的根
  4. 更新parent和cur的平衡因子
//parent->_balanceFactor == 2 && cur->_balanceFactor = 1
void leftSingleRotate(node* parent) //左单旋
{
  //记录指针
  node* parent_RightChild = parent->_right; //parent_RightChild = cur
  node* parent_RightChild_LeftChild = parentRightChild->_left; //parent_RightChild_LeftChild = curLeft
  node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
  parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
  if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
  {
    parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
  }
  parent_RightChild->_left = parent; //让parent放在cur的左子树位置
  parent->_parent = parent_RightChild; //parent的父节点指向指向cur
  //cur(parent_RightChild)变为子树或者整颗树的根
  if (parent_parent == nullptr) //parent是整颗树的根
  {
    _root = parent_RightChild;
    _root->_parent = nullptr;
  }
  else //parent是局部子树的根
  {
    if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
    {
      parent_parent->_left = parent_RightChild;
    }
    else //parent节点在父节点的右子树位置
    {
      parent_parent->_right = parent_RightChild;
    }
    parent_RightChild->_parent = parent_parent;
  }
  //更新平衡因子
  parent->_balanceFactor = parent_RightChild->_balanceFactor = 0;
}
  • 右单旋
  • 右单旋一定是左高右低
  • 微信图片_20230523235504.png

微信图片_20230523235521.png

微信图片_20230523235537.png

三个举例来抽象得到宏观图

微信图片_20230523235713.pngH为这颗子树高度,当H为0,反应就是上述第一个例子;当H为1,反应就是上述第二个例子;当H为2,反应就是上述第三个例子。当H为x时,其调整方法不变。当H为2时,这里的c子树必须是x类型的树,此时有三种情况的高度为2的树:


微信图片_20230523235738.png

如何旋转?

微信图片_20230523235757.png

  1. 让cur的右子树(curRight)放在parent的左子树位置并且让curRight父节点的指向指向parent
  2. 让parent放在cur的右子树位置并且让parent的父节点指向指向cur
  3. cur变为子树或者整颗树的根
  4. 更新parent和cur的平衡因子
//parent->_balanceFactor == -2 && cur->_balanceFactor == -1
void rightSingleRotate(node* parent) //右单旋
{
    //记录指针
    node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
    node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
    node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
    parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
    if (parent_LeftChild_RightChild != nullptr)
    {
        parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
    }
    parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
    parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur
    //cur(parent_LeftChild)变为子树或者整颗树的根
    if (parent_parent == nullptr) //parent是整颗树的根
    {
        _root = parent_LeftChild; //cur(parent_LeftChild)就是根
        _root->_parent = nullptr;
    }
    else //parent是局部子树的根
    {
        if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
        {
            parent_parent->_left = parent_LeftChild;
        }
        else //parent节点在父节点的右子树位置
        {
            parent_parent->_right = parent_LeftChild;
        }
        parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
    }
    //更新平衡因子
    parent->_balanceFactor = parent_LeftChild->_balanceFactor = 0;
}
  • 先左后右旋
  • 微信图片_20230523235844.png

抽象图:

微信图片_20230523235908.png

H为子树高度,H为0时,此时60这个节点也不存在,新增节点必须是key为60这个节点位置;H为1,2,3…等等图形这里不再画出,都可以通过抽象达成相同调整方法,这里被插入节点一定是b,c子树位置或者b,c的子树孩子位置。


如何旋转?

微信图片_20230523235948.png

先左旋cur为根的子树

让cur的右指向指向curRightLeft并且curRightLeft的父节点的指向指向cur

让curRight的左指向指向cur并且cur的父节点的指向指向curRight

curRight作为子树根并且改变curRight的父节点的指向指向parent

更新cur和curRight的平衡因子为0

后右旋parent为根的子树

让parent的左指向指向curRightRight并且curRightRight的父节点的指向指向parent

让curRight的右子树指向parent并且parent的父节点的指向指向curRight

curRight作为树的根并且改变curRight的父节点指向指向空

更新parent和curRight的平衡因子为0

更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)

c子树插入(curRight->_balanceFactor=1):最终cur的平衡因子为-1,parent平衡因子为0,curRight平衡因子为0

b子树插入(curRight->_balanceFactor=-1):最终cur的平衡因子为0,parent平衡因子为1,curRight平衡因子为0

curRight本身是被插入节点(curRight->_balanceFactor=0,H=0),最终cur、parent、curRight平衡因子都为0

//parent->_balanceFactor == -2 && cur->_balanceFactor == 1
void leftRightRotate(node* parent)
{
    node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
    node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
    int balanceFactor = parent_LeftChild_RightChild->_balanceFactor; //记录curRight的平衡因子值:确定在b子树还是c子树插入
    leftSingleRotate(parent->_left); //左单旋cur为根树
    rightSingleRotate(parent); //右单旋parent为根的树
    //更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
    if (balanceFactor == 1) //c子树插入
    {
        parent->_balanceFactor = 0;
        parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
        parent_LeftChild = -1; //cur->_balanceFactor = -1
    }
    else if (balanceFactor == -1) //b子树插入
    {
        parent->_balanceFactor = 1;
        parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
        parent_LeftChild = 0;  //cur->_balanceFactor = 0
    }
    else if (balanceFactor == 0) //curRight自身是被插入节点
    {
        parent->_balanceFactor = 0;
        parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
        parent_LeftChild = 0;  //cur->_balanceFactor = 0
    }
    else
    {
        assert(false);
    }
}
  • 先右后左旋

抽象图:

微信图片_20230524000057.png

H为子树高度,H为0时,此时60这个节点也不存在,新增节点必须是key为60这个节点位置;H为1,2,3…等等图形这里不再画出,都可以通过抽象达成相同调整方法,这里被插入节点一定是b,c子树位置或者b,c的子树孩子位置。

如何旋转?

微信图片_20230524000838.png

先右旋cur为根的子树

让cur的左指向指向curLeftRight并且curLeftRight的父节点的指向指向cur

让curLeft的右指向指向cur并且cur的父节点的指向指向curLeft

curLeft作为子树的根并且curLeft的父节点的指向指向parent

更新cur和curLeft的平衡因子为0

后左旋parent为根的树

让parent的右指向指向curLeftLeft并且curLeftLeft的父节点的指向指向parent

让curLeft的左指向指向parent并且paren的父节点的指向指向curLeft

curLeft作为树的根并且改变curLeft的父节点的指向指向nullptr

更新parent和curLeft的平衡因子为0

更新cur、curLeft、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)

b子树插入(curLeft->_balance=1),最终cur的平衡因子为0,parent平衡因子为-1,curLeft平衡因子为0

c子树插入(curLeft->_balance=-1),最终cur的平衡因子为1,parent平衡因子为0,curLeft平衡因子为0

curLeft本身是被插入节点(curLeft->_balanceFactor=0,H=0),最终cur、parent、curRight平衡因子都为0

//parent->_balanceFactor == 2 && cur->_balanceFactor == -1
void rightLeftRotate(node* parent) //先右后左旋
{
    node* parent_RightChild = parent->_right; //parent_RightChild = cur
    node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
    int balanceFactor = parent_RightChild_LeftChild->_balanceFactor; 记录curLeft的平衡因子值:确定在b子树还是c子树插入
    rightSingleRotate(parent_RightChild); //右单旋cur为根的树
    leftSingleRotate(parent); //左单旋parent为根的树
    //更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
    if (balanceFactor == 1) //b子树插入
    {
        parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
        parent->_balanceFactor = -1;
        parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
    }
    else if (balanceFactor == -1) //c子树插入
    {
        parent_RightChild->_balanceFactor = 1; //cur->_balanceFactor = 1
        parent->_balanceFactor = 0;
        parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
    }
    else if (balanceFactor == 0) //curLeft本身是被插入节点
    {
        parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
        parent->_balanceFactor = 0;
        parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
    }
    else
    {
        assert(false);
    }
}

5. 检测

  1. 中序遍历是否升序
void inOrder() //中序遍历
{
    _inOrder(_root);
    cout << endl;
}
void _inOrder(node* root) //中序遍历
{
  if (root == nullptr)
    {
        return;
    }
    _inOrder(root->_left);
    cout << root->_couple.first << " ";
    _inOrder(root->_right);
}
  1. 是否平衡
bool isBalance() //判断树是否为平衡树
{
    return _isBalance(_root);
}
int heightDiffer(node* root)
{
    if (root == nullptr)
        return 0;
    int leftHeight = heightDiffer(root->_left);
    int rightHeight = heightDiffer(root->_right);
    return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; 
}
bool _isBalance(node* root) //判断树是否为平衡树(检查每个节点的左右子树高度差)
{
    if (root == nullptr)
        return true;
    int leftHeight = heightDiffer(root->_left);
    int rightHeight = heightDiffer(root->_right);
    if (rightHeight - leftHeight != root->_balanceFactor) //不只是对高度检查还应该对平衡因子检查
    {
        cout << "key:" << root->_couple.first << " node balanceFactor exception!" << endl;
        return false;
    }
    return abs(leftHeight - rightHeight) < 2 
        && _isBalance(root->_left) 
        && _isBalance(root->_right);
}

6. 完整代码

插入阶段


根节点为空

new节点,改变根指向,返回true

根节点不为空

找到插入位置

右查找:当前节点key值 < 插入节点key值

左查找:当前节点key值 > 插入节点key值

当前节点key值 = 插入节点key值 :直接返回false

在对应待插入位置插入

new节点,当前插入位置指向该节点

右插入:当前节点key值 < 插入节点key值

左插入: 当前节点key值 > 插入节点key值

当前被插入节点父指针指向指向被连接节点

自动平衡

更新平衡因子

改变被插入节点父节点平衡因子

被插入节点在其父节点右子树中:父节点平衡因子+1

被插入节点在其父节点左子树中:父节点平衡因子-1

判断是否继续向上更新平衡因子

父节点平衡因子为1或者-1,继续更新

父节点平衡因子为0,无需更新

父节点平衡因子为2或者-2,子树不平衡,旋转处理使得平衡(旋转处理)

parent->_balanceFactor == 2 && cur->_balanceFactor == 1 --> 左单旋

parent->_balanceFactor == -2 && cur->_balanceFactor == -1 --> 右单旋

parent->_balanceFactor == -2 && cur->_balanceFactor == 1 --> 先左后右旋

parent->_balanceFactor == 2 && cur->_balanceFactor == -1 --> 先右后左旋


#pragma once
#include <iostream>
#include <utility>
#include <cassert>
using namespace std;
template <class KEY, class VAULE>
struct AVLtree_node
{
    AVLtree_node<KEY, VAULE>* _left; //左节点指向
    AVLtree_node<KEY, VAULE>* _right; //右节点指向
    AVLtree_node<KEY, VAULE>* _parent; //父节点指向
    pair<KEY, VAULE> _couple; //存储key/value
    int _balanceFactor; //平衡因子(左右子树高度差)
    AVLtree_node(const pair<KEY, VAULE>& couple)
        :_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_couple(couple)
        ,_balanceFactor(0)
    {}
};
template <class KEY, class VAULE>
class AVLtree
{
    typedef AVLtree_node<KEY, VAULE> node;
public:
    bool insert(const pair<KEY, VAULE>& couple)
    {
        if (_root == nullptr) //根为空:直接new并指向返回
        {
            _root = new node(couple);
            return true;
        }
        /*找插入位置*/
        node* parent = nullptr; //起初根节点的父节点为nullptr
        node* cur = _root; //被插入节点指向
        while (cur)
        {
            if (cur->_couple.first < couple.first) //右查找:当前节点key值 < 插入节点key值
            {
                parent = cur;
                cur = cur->_right;
            }
            else if (cur->_couple.first > couple.first) //左查找: 当前节点key值 > 插入节点key值
            {
                parent = cur;
                cur = cur->_left;
            }
            else //当前节点key值 = 插入节点key值:直接退出
            {
                return false;
            }
        }
        /*在对应位置插入*/
        cur = new node(couple);
        if (parent->_couple.first > couple.first) //左插入: 当前节点key值 > 插入节点key值
        {
            parent->_left = cur;
        }
        else //右插入:当前节点key值 < 插入节点key值
        {
            parent->_right = cur;
        }
        cur->_parent = parent; //反向链接
        /*自动平衡 TODO*/
        //更新平衡因子
        while (parent)
        {
            if (cur == parent->_right) //被插入节点在其父节点右子树中
            {
                parent->_balanceFactor++;
            }
            else //被插入节点在其父节点左子树中
            {
                parent->_balanceFactor--;
            }
            if (parent->_balanceFactor == 1 || parent->_balanceFactor == -1) //继续更新平衡因子
            {
                parent = parent->_parent;
                cur = cur->_parent;
            }
            else if (parent->_balanceFactor == 0)
            {
                break;
            }
            else if (parent->_balanceFactor == 2 || parent->_balanceFactor == -2) //子树不平衡(旋转处理)
            {
                if (parent->_balanceFactor == 2 && cur->_balanceFactor == 1) //左单旋
                {
                    leftSingleRotate(parent);
                }
                else if (parent->_balanceFactor == -2 && cur->_balanceFactor == -1) //右单旋
                {
                    rightSingleRotate(parent);
                }
                else if (parent->_balanceFactor == -2 && cur->_balanceFactor == 1) //先左后右旋
                {
                    leftRightRotate(parent);
                }
                else if (parent->_balanceFactor == 2 && cur->_balanceFactor == -1) //先右后左旋
                {
                    rightLeftRotate(parent);
                }
                else
                {
                    assert(false);
                }
                //旋转可以达到左右子树平衡并且降低高度
                break;
            }
            else
            {
                assert(false);
            }
        }
        return true;
    }
    void inOrder() //中序遍历
    {
        _inOrder(_root);
        cout << endl;
    }
    bool isBalance() //判断树是否为平衡树
    {
        return _isBalance(_root);
    }
private:
    void leftSingleRotate(node* parent) //左单旋
    {
        //记录指针
        node* parent_RightChild = parent->_right; //parent_RightChild = cur
        node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
        parent->_right = parent_RightChild_LeftChild; //让cur的左子树(curLeft)放在parent的右子树位置
        if (parent_RightChild_LeftChild != nullptr) //H为0时,parent_RightChild_LeftChild=nullptr
        {
            parent_RightChild_LeftChild->_parent = parent; //curLeft的父节点指向指向parent
        }
        parent_RightChild->_left = parent; //让parent放在cur的左子树位置
        parent->_parent = parent_RightChild; //parent的父节点指向指向cur
        //cur(parent_RightChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_RightChild; //cur(parent_RightChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_RightChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_RightChild;
            }
            parent_RightChild->_parent = parent_parent; //cur(parent_RightChild)指向局部根的父节点
        }
        //更新平衡因子
        parent->_balanceFactor = parent_RightChild->_balanceFactor = 0;
    }
    void rightSingleRotate(node* parent) //右单旋
    {
        //记录指针
        node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
        node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
        node* parent_parent = parent->_parent; //局部根或整棵树根的父节点
        parent->_left = parent_LeftChild_RightChild; //让cur的右子树(curRight)放在parent的左子树位置
        if (parent_LeftChild_RightChild != nullptr)
        {
            parent_LeftChild_RightChild->_parent = parent; //让curRight父节点的指向指向parent
        }
        parent_LeftChild->_right = parent; //让parent放在cur的右子树位置
        parent->_parent = parent_LeftChild; //让parent的父节点指向指向cur
        //cur(parent_LeftChild)变为子树或者整颗树的根
        if (parent_parent == nullptr) //parent是整颗树的根
        {
            _root = parent_LeftChild; //cur(parent_LeftChild)就是根
            _root->_parent = nullptr;
        }
        else //parent是局部子树的根
        {
            if (parent_parent->_left == parent) //parent节点在父节点的左子树位置
            {
                parent_parent->_left = parent_LeftChild;
            }
            else //parent节点在父节点的右子树位置
            {
                parent_parent->_right = parent_LeftChild;
            }
            parent_LeftChild->_parent = parent_parent; //cur(parent_LeftChild)指向局部根的父节点
        }
        //更新平衡因子
        parent->_balanceFactor = parent_LeftChild->_balanceFactor = 0;
    }
    void leftRightRotate(node* parent) //先左后右旋
    {
        node* parent_LeftChild = parent->_left; //parent_LeftChild = cur
        node* parent_LeftChild_RightChild = parent_LeftChild->_right; //parent_LeftChild_RightChild = curRight
        int balanceFactor = parent_LeftChild_RightChild->_balanceFactor; //记录curRight的平衡因子值:确定在b子树还是c子树插入
        leftSingleRotate(parent->_left); //左单旋cur为根的树
        rightSingleRotate(parent); //右单旋parent为根的树
        //更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
        if (balanceFactor == 1) //c子树插入
        {
            parent->_balanceFactor = 0; 
            parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
            parent_LeftChild->_balanceFactor = -1; //cur->_balanceFactor = -1
        }
        else if (balanceFactor == -1) //b子树插入
        {
            parent->_balanceFactor = 1;
            parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
            parent_LeftChild->_balanceFactor = 0;  //cur->_balanceFactor = 0
        }
        else if (balanceFactor == 0) //curRight自身是被插入节点
        {
            parent->_balanceFactor = 0;
            parent_LeftChild_RightChild = 0; //curRight->_balanceFactor = 0
            parent_LeftChild->_balanceFactor = 0;  //cur->_balanceFactor = 0
        }
        else
        {
            assert(false);
        }
    }
    void rightLeftRotate(node* parent) //先右后左旋
    {
        node* parent_RightChild = parent->_right; //parent_RightChild = cur
        node* parent_RightChild_LeftChild = parent_RightChild->_left; //parent_RightChild_LeftChild = curLeft
        int balanceFactor = parent_RightChild_LeftChild->_balanceFactor; 记录curLeft的平衡因子值:确定在b子树还是c子树插入
        rightSingleRotate(parent_RightChild); //右单旋cur为根的树
        leftSingleRotate(parent); //左单旋parent为根的树
        //更新cur、curRight、parent平衡因子(插入b,c子树不同位置会导致最终平衡因子的变化)
        if (balanceFactor == 1) //b子树插入
        {
            parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
            parent->_balanceFactor = -1;
            parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
        }
        else if (balanceFactor == -1) //c子树插入
        {
            parent_RightChild->_balanceFactor = 1; //cur->_balanceFactor = 1
            parent->_balanceFactor = 0; 
            parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
        }
        else if (balanceFactor == 0) //curLeft本身是被插入节点
        {
            parent_RightChild->_balanceFactor = 0; //cur->_balanceFactor = 0
            parent->_balanceFactor = 0;
            parent_RightChild_LeftChild->_balanceFactor = 0; //curLeft->_balanceFactor = 0;
        }
        else
        {
            assert(false);
        }
    }
    void _inOrder(node* root) //中序遍历
    {
        if (root == nullptr)
        {
            return;
        }
        _inOrder(root->_left);
        cout << root->_couple.first << " ";
        _inOrder(root->_right);
    }
    int heightDiffer(node* root)
    {
        if (root == nullptr)
            return 0;
        int leftHeight = heightDiffer(root->_left);
        int rightHeight = heightDiffer(root->_right);
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1; 
    }
    bool _isBalance(node* root) //判断树是否为平衡树(检查每个节点的左右子树高度差)
    {
        if (root == nullptr)
            return true;
        int leftHeight = heightDiffer(root->_left);
        int rightHeight = heightDiffer(root->_right);
        if (rightHeight - leftHeight != root->_balanceFactor) //不只是对高度检查还应该对平衡因子检查
        {
            cout << "key:" << root->_couple.first << " node balanceFactor exception!" << endl;
            return false;
        }
        return abs(leftHeight - rightHeight) < 2 
            && _isBalance(root->_left) 
            && _isBalance(root->_right);
    }
private:
    node* _root = nullptr;
};
/*测试*/
void test_AVLtree1()
{
    //int arr[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
    int arr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14};
    AVLtree<int, int> tree;
    for (auto x : arr)
    {
        tree.insert(make_pair(x, x));
    }
    tree.inOrder(); //中序遍历
    cout << tree.isBalance() << endl; //判断是否为平衡树
}
void test_perfermance() //测试性能
{
    srand(time(nullptr));
    const size_t N = 100000;
    AVLtree<int, int> tree;
    for (size_t i = 0; i < N; ++i)
    {
        size_t x = rand();
        tree.insert(make_pair(x, x));
    }
    cout << tree.isBalance() << endl; 
}























相关文章
|
1天前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
12 2
|
2月前
|
存储 C++
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
27 2
|
3月前
|
C++ 容器
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
34 5
|
4月前
|
C++
【C++】手撕AVL树(下)
【C++】手撕AVL树(下)
51 1
|
4月前
|
算法 测试技术 C++
【C++高阶】掌握AVL树:构建与维护平衡二叉搜索树的艺术
【C++高阶】掌握AVL树:构建与维护平衡二叉搜索树的艺术
35 2
|
4月前
|
Java C++ Python
【C++】手撕AVL树(上)
【C++】手撕AVL树(上)
52 0
|
5天前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
25 5
|
11天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
40 4
|
12天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
36 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4

热门文章

最新文章

下一篇
无影云桌面