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; 
}























目录
打赏
0
0
0
0
0
分享
相关文章
|
2月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
57 10
|
2月前
|
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
【数据结构——树】哈夫曼树(头歌实践教学平台习题)【合集】目录 任务描述 相关知识 测试说明 我的通关代码: 测试结果:任务描述 本关任务:编写一个程序构建哈夫曼树和生成哈夫曼编码。 相关知识 为了完成本关任务,你需要掌握: 1.如何构建哈夫曼树, 2.如何生成哈夫曼编码。 测试说明 平台会对你编写的代码进行测试: 测试输入: 1192677541518462450242195190181174157138124123 (用户分别输入所列单词的频度) 预
70 14
【C++数据结构——树】哈夫曼树(头歌实践教学平台习题) 【合集】
|
2月前
|
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
60 12
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
55 2
|
4月前
|
【C++】AVL树
AVL树是一种自平衡二叉搜索树,由Georgy Adelson-Velsky和Evgenii Landis提出。它通过确保任意节点的两子树高度差不超过1来维持平衡,支持高效插入、删除和查找操作,时间复杂度为O(log n)。AVL树通过四种旋转操作(左旋、右旋、左-右旋、右-左旋)来恢复树的平衡状态,适用于需要频繁进行数据操作的场景。
91 2
|
6月前
|
【C++】AVL树
AVL树是一种自平衡二叉搜索树:它以苏联科学家Georgy Adelson-Velsky和Evgenii Landis的名字命名。
53 2
|
7月前
|
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
【C++航海王:追寻罗杰的编程之路】关联式容器的底层结构——AVL树
63 5
|
8月前
|
C++
【C++】手撕AVL树(下)
【C++】手撕AVL树(下)
72 1
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。

热门文章

最新文章

  • 1
    小鱼深度评测 | 通义灵码2.0,不仅可跨语言编码,自动生成单元测试,更炸裂的是集成DeepSeek模型且免费使用,太炸裂了。
    28147
  • 2
    3天功能开发→3小时:通义灵码2.0+DEEPSEEK实测报告,单元测试生成准确率92%的秘密
    35
  • 3
    Potpie.ai:比Copilot更狠!这个AI直接接管项目代码,自动Debug+测试+开发全搞定
    12
  • 4
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    23
  • 5
    基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
    3
  • 6
    大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
    10
  • 7
    「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
    12
  • 8
    用户说 | 通义灵码2.0,跨语言编码+自动生成单元测试+集成DeepSeek模型且免费使用
    17
  • 9
    阿里云零门槛、轻松部署您的专属 DeepSeek模型体验测试
    30
  • 10
    以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
    7
  • AI助理

    你好,我是AI助理

    可以解答问题、推荐解决方案等