当前位置:   article > 正文

C++——AVL树及其模拟实现_不使用stl实现avl树

不使用stl实现avl树

目录

一、AVL树的概念

二、AVL树的插入

1、AVL树节点的定义

2、AVL树的插入

三、AVL树的旋转

1、新节点插入较高左子树的左侧——左左:右单旋

2、新节点插入较高右子树的右侧——右右:左单旋

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

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

四、AVL树的验证

 五、AVL树的性能


一、AVL树的概念

        搜索二叉树虽然可以缩短查找效率,但是数据如果有序或接近有序搜索二叉树将退化为单只树,查找元素相当于在顺序表中搜索元素,效率低下。所以有了平衡二叉树,当向二叉树中插入新节点后,如果能保证每个节点的左右子树高度差的绝对值不超过1,即可降低树的高度,从而减少平均搜索长度。

 一颗AVL树或者是空树,或者是具有以下性质的搜索二叉树:

        ·它的左右子树都是AVL树

        ·左右子树高度差的绝对值不超过1

        如果一颗搜索二叉树是平衡的,他就是AVL树。如果它有n个节点,其高度可保持在O(log2n),搜索时间复杂度O(log2n)。

二、AVL树的插入

1、AVL树节点的定义

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

2、AVL树的插入

  1. bool Insert(const pair<K, V>& kv)
  2. {
  3. if (_root == nullptr)
  4. {
  5. _root = new Node(kv);
  6. return true;
  7. }
  8. Node* parent = nullptr;
  9. Node* cur = _root;
  10. //找到要插入的节点
  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->_left = cur;
  32. }
  33. else
  34. {
  35. parent->_right = cur;
  36. }
  37. cur->_parent = parent;
  38. //更新平衡因子
  39. while (parent)
  40. {
  41. if (cur == parent->_right)
  42. {
  43. parent->_bf++;
  44. }
  45. else
  46. {
  47. parent->_bf--;
  48. }
  49. if (parent->_bf == 1 || parent->_bf == -1)
  50. {
  51. //继续想上更新
  52. parent = parent->_parent;
  53. cur = cur->_parent;
  54. }
  55. else if (parent->_bf == 0)
  56. {
  57. break;
  58. }
  59. else if (parent->_bf == 2 || parent->_bf == -1)
  60. {
  61. //进行旋转处理
  62. if (parent->_bf == 2 && cur->_bf == 1)
  63. {
  64. RotateL(parent); //右边引起的高度变高进行左旋
  65. }
  66. else if (parent->_bf == -2 && cur->_bf == -1)
  67. {
  68. RotateR(parent); //左边引起的高度变高进行右旋
  69. }
  70. else if (parent->_bf == 2 && cur->_bf == -1)
  71. {
  72. RotateRL(parent); //插入在右子树的左子树引起的高度变高先右旋在左旋
  73. }
  74. else if (parent->_bf == -2 && cur->_bf == 1)
  75. {
  76. RotateLR(parent); //插入在左子树的右子树引起的高度变高先左旋再右旋
  77. }
  78. else
  79. {
  80. assert(false);
  81. }
  82. break;
  83. }
  84. else
  85. {
  86. assert(false);
  87. }
  88. }
  89. return true;
  90. }

三、AVL树的旋转

1、新节点插入较高左子树的左侧——左左:右单旋

        上图插入前,AVL树是平衡的,新节点插入到30的左子树中,30的左子树高度增加,导致60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树上提,60转下来60变为30的右子树,如果30有右子树,右子树根的值一定大于30,小于60,所以放在60的左子树,旋转完成后更新平衡因子。

旋转过程中有以下几种情况需要考虑

        ·30节点的右孩子可能存在,也可能不存在

        ·60可能是根节点,也可能是子树,如果是根节点,旋转完成后要更新根节点,如果是子树,可能是某个节点的左子树也可能是右子树 

  1. void RotateR(Node* parent)
  2. {
  3. Node* subL = parent->_left;
  4. Node* subLR = subL->_right;
  5. parent->_left = subLR;
  6. if (subLR)
  7. subLR->_parent = parent;
  8. Node* ppnode = parent->_parent;
  9. subL->_right = parent;
  10. parent->_parent = subL;
  11. if (parent == _root)
  12. {
  13. _root = subL;
  14. _root->_parent = nullptr;
  15. }
  16. else
  17. {
  18. if (ppnode->_left == parent)
  19. {
  20. ppnode->_left = subL;
  21. }
  22. else
  23. {
  24. ppnode->_right = subL;
  25. }
  26. subL->_parent = ppnode;
  27. }
  28. subL->_bf = parent->_bf = 0;
  29. }

2、新节点插入较高右子树的右侧——右右:左单旋

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

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

  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);
  7. RotateR(parent);
  8. if (bf == 1)
  9. {
  10. parent->_bf = 0;
  11. subLR->_bf = 0;
  12. subL->_bf = -1;
  13. }
  14. else if (bf == -1)
  15. {
  16. parent->_bf = 1;
  17. subLR->_bf = 0;
  18. subL->_bf = 0;
  19. }
  20. else if (bf == 0)
  21. {
  22. parent->_bf = 0;
  23. subLR->_bf = 0;
  24. subL->_bf = 0;
  25. }
  26. else
  27. {
  28. assert(false);
  29. }
  30. }

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

  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 == 1)
  9. {
  10. subR->_bf = 0;
  11. parent->_bf = -1;
  12. subRL->_bf = 0;
  13. }
  14. else if (bf == -1)
  15. {
  16. subR->_bf = 1;
  17. parent->_bf = 0;
  18. subRL->_bf = 0;
  19. }
  20. else if (bf == 0)
  21. {
  22. subR->_bf = 0;
  23. parent->_bf = 0;
  24. subRL->_bf = 0;
  25. }
  26. else
  27. {
  28. assert(false);
  29. }
  30. }

四、AVL树的验证

1、验证其为搜索二叉树

        如果中序遍历可得到一个有序的序列,就说明为搜索二叉树

2、验证其为平衡树

  1. bool _IsBalance(Node* root)
  2. {
  3. if (root == nullptr)
  4. {
  5. return true;
  6. }
  7. int leftH = _Height(root->_left);
  8. int rightH = _Height(root->_right);
  9. if (rightH - leftH != root->_bf)
  10. {
  11. cout << root->_kv.first << "节点平衡因子异常" << endl;
  12. return false;
  13. }
  14. return abs(leftH - rightH) < 2
  15. && _IsBalance(root->_left)
  16. && _IsBalance(root->_right);
  17. }

 五、AVL树的性能

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/919468
推荐阅读
相关标签
  

闽ICP备14008679号