• AVL数的实现


    目录

    1.介绍:

    2.实现

    2.1.AVL数的结点实现

    2.2AVL数的插入

    2.2.1更新平衡因子

    2.2.2 4种旋转


    1.介绍:

    前面已经介绍过了二叉搜索树,使用二叉搜索树可以缩短查找数据的时间,但如果数据是有序的序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

    种解决上述问题的方法: 当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过 1( 需要对树中的结点进行调整 )。
    例如:

    2.实现

    2.1.AVL数的结点实现

    为了更好的调整结点,会定义为三叉链

    1. template<class K, class V>
    2. struct AVLTreeNode
    3. {
    4. pair _kv;
    5. AVLTreeNode* _left;
    6. AVLTreeNode* _right;
    7. AVLTreeNode* _parent;
    8. int _bf; // balance factor
    9. AVLTreeNode(const pair& kv)
    10. :_kv(kv)
    11. , _left(nullptr)
    12. , _right(nullptr)
    13. , _parent(nullptr)//指向该结点的父亲
    14. , _bf(0)
    15. {}
    16. };

    2.2AVL数的插入

    分为两部分:

    1. 按照二叉搜索树的方式插入新节点
    2. 调整节点的平衡因子
    先插入结点,按照二叉搜索树规则插入
    1. bool Insert(const pair& kv)
    2. {
    3. if (_root == nullptr)
    4. {
    5. _root = new Node(kv);
    6. _root->_bf = 0;
    7. return true;
    8. }
    9. Node* parent = nullptr;
    10. Node* cur = _root;
    11. while (cur)
    12. {
    13. if (cur->_kv.first < kv.first)
    14. {
    15. parent = cur;
    16. cur = cur->_right;
    17. }
    18. else if (cur->_kv.first > kv.first)
    19. {
    20. parent = cur;
    21. cur = cur->_left;
    22. }
    23. else
    24. {
    25. return false;
    26. }
    27. }
    28. cur = new Node(kv);
    29. if (parent->_kv.first < kv.first)
    30. {
    31. parent->_right = cur;
    32. }
    33. else
    34. {
    35. parent->_left = cur;
    36. }
    37. cur->_parent = parent;//是三叉链,要把新插入结点的父亲链接上
    38. }

    当把结点插入之后,平衡因子也要相继做出调整。

    2.2.1更新平衡因子

    当插入一个新结点,该结点的平衡因子是0,若该节点是父亲结点的右子树,那么父亲结点的平衡因子要+1,若是左子树,则-1。(规定不是一定的)。   

    如果父亲结点的平衡因子发生变动,  1(父亲结点的平衡因子变为1或-1,说明以父亲结点为子树的高度发生了变化,平衡因子还需往上更新)   2(父亲结点的平衡因子变为0,说明以父亲结点为子树的高度未发生变化,无需再做任何调整)   3(父亲结点的平衡因子变为-2或2,说明以父亲结点为子树已不满足二叉搜索树的规则,要对结点进行旋转调整)

    举例:此时这棵树满足规则

     插入结点后:

     代码实现:

    1. while (parent)
    2. {
    3. if (cur == parent->_right)该节点是父亲结点的右子树,父亲结点平衡因子+1
    4. {
    5. parent->_bf++;
    6. }
    7. else
    8. {
    9. parent->_bf--;
    10. }
    11. if (parent->_bf == 0) //父亲结点的平衡因子变为0,说明以父亲结点为子树的高度未发生
    12. 变化,无需再做任何调整
    13. {
    14. break;
    15. }
    16. else if (parent->_bf == 1 || parent->_bf == -1),平衡因子往上更新,最多更新到根
    17. {
    18. cur = cur->_parent;
    19. parent = parent->_parent;
    20. }
    21. }

    2.2.2 4种旋转

    二叉搜索树中插入新结点后,保证每个结点的左右子树高度之差的绝对值不超过1,如果某个结点的平衡=2或-2,就要对结点进行调整。

    1.右单旋(处理LL型违规)

     把parent->left=subLR;subL->right->parent;还要把parent,subL的平衡因子置为0。

    代码实现:

    1. void RotateR(Node* parent)
    2. {
    3. Node* subL = parent->_left;
    4. Node* subLR = subL->_right;
    5. parent->_left = subLR;
    6. if (subLR)//subLR可能为空,例如上图第一种情况
    7. subLR->_parent = parent;
    8. Node* ppNode = parent->_parent;//先保留parent的父亲结点
    9. subL->_right = parent;
    10. parent->_parent = subL;
    11. if (parent == _root)//如果parent是根结点,则把subL置为根结点,并把它的parent置为空
    12. {
    13. _root = subL;
    14. _root->_parent = nullptr;
    15. }
    16. else
    17. {
    18. if (ppNode->_left == parent)//如果parent不是根节点,调整后,还需链接subL和ppNode
    19. 的关系
    20. {
    21. ppNode->_left = subL;
    22. }
    23. else
    24. {
    25. ppNode->_right = subL;
    26. }
    27. subL->_parent = ppNode;
    28. }
    29. subL->_bf = parent->_bf = 0;//把二者的平衡因子置为0;
    30. }

    2. 左单旋(处理RR型违规)

     与左单旋类似,把parent->right=subRL,subR->left=parent;也要调节它们的父亲关系

    代码实现:

    1. void RotateL(Node* parent)
    2. {
    3. Node* pparent = parent->_parent;
    4. Node* subR = parent->_right;
    5. Node* subRL = subR->_left;
    6. parent->_right = subRL;
    7. subR->_left = parent;
    8. parent->_parent = subR;
    9. if (subRL)
    10. subRL->_parent = parent;
    11. if (parent == _root)
    12. {
    13. subR->_parent = nullptr;
    14. }
    15. else
    16. {
    17. if (parent == pparent->_left)
    18. pparent->_left = subR;
    19. else
    20. pparent->_right = subR;
    21. subR->_parent = pparent;
    22. }
    23. parent->_bf = 0;
    24. subR->_bf = 0;
    25. }

     

     3.左右单旋

    当parent的平衡因子=2,但subL的平衡因子=-1时。

    先将subL为根节点的子树左旋,再将以parent为根的树右旋。

    但与单旋不同,双旋后平衡因子会有多种组合结果。(以subLR的平衡因子进行判断)

     

    代码实现:

    1. void RotateLR(Node* parent)
    2. {
    3. Node* subL = parent->_left;
    4. Node* subLR = subL->_right;
    5. int bf = subLR->_bf;
    6. RotateL(parent->_left);//先将subL为根节点的子树左旋
    7. RotateR(parent);//再将以parent为根的树右旋。
    8. if (bf == 0)
    9. {
    10. parent->_bf = 0;
    11. subL->_bf = 0;
    12. subLR->_bf = 0;
    13. }
    14. else if (bf == 1)
    15. {
    16. parent->_bf = -1;
    17. subL->_bf = 0;
    18. subLR->_bf = 0;
    19. }
    20. else if (bf == -1)
    21. {
    22. parent->_bf = 0;
    23. subL->_bf = 1;
    24. subLR->_bf = 0;
    25. }
    26. else
    27. {
    28. // subLR->_bf旋转前就有问题
    29. assert(false);
    30. }
    31. }

    4.右左旋转

    当parent的平衡因子=-2,但subR的平衡因子=1时。

    先将subR为根节点的子树右旋,再将以parent为根的树左旋。

    但与单旋不同,双旋后平衡因子会有多种组合结果。(以subRL的平衡因子进行判断)

    代码实现:

    1. void RotateRL(Node* parent)
    2. {
    3. Node* subR = parent->_right;
    4. Node* subRL = subR->_left;
    5. int bf = subRL->_bf;
    6. RotateR(parent->_right);
    7. RotateL(parent);
    8. if (bf == 0)
    9. {
    10. subRL->_bf = 0;
    11. parent->_bf = 0;
    12. subR->_bf = 0;
    13. }
    14. else if (bf == -1)
    15. {
    16. subRL->_bf = 0;
    17. parent->_bf = 1;
    18. subR->_bf = 0;
    19. }
    20. else if (bf == 1)
    21. {
    22. subRL->_bf = 0;
    23. parent->_bf = 0;
    24. subR->_bf = -1;
    25. }
    26. else
    27. {
    28. assert(false);
    29. }
    30. }

  • 相关阅读:
    LeetCode 278. 第一个错误的版本
    Nginx +tomcat的集群概念
    基于SpringBoot+Vue+微信小程序的电影平台
    重新理解微服务之它还那么纯粹吗?
    C++笔记 - - list的模拟实现和使用
    AX9000使用docker实现aria2下载
    深度学习:pytorch nn.Embedding详解
    利用STM32CubeMX和Keil模拟器,3天入门FreeRTOS(5.4) —— 事件组
    非类型模板参数+模板的特化
    防薅图鉴之一薅羊毛深似海,牢底坐穿后悔晚
  • 原文地址:https://blog.csdn.net/m0_64397669/article/details/126278560