• [ 数据结构 - C++] AVL树原理及实现


    问题引入:

    在上文我们提到了二叉搜索树在按值顺序插入时,会形成单边树,会大大降低二叉搜索树的性能。因此我们要将二叉搜索树进行平衡,采取适当的平衡措施,从而减低二叉搜索树的高度,使二叉搜索树达到一个接近于完全二叉树的样子(如下图所示),提高二叉搜索树的性能。本节我们要介绍的平衡树为AVL树。

    目录

    1.AVL树

    1.1 AVL树的概念

    2.AVL树节点的定义

    3.AVL树的插入与旋转

    3.1 左单旋

    代码实现左单旋

    3.2 右单旋

    代码实现右单旋

    3.3 先左单旋再右单旋

    左右双旋代码实现:

    3.4 先右单旋再左单旋

    右左双旋代码实现

    Insert实现代码

    4.判断一棵树是否是AVL树

    验证AVL树代码实现:

    5.AVL树的验证与查看

    验证与查看:

    1.顺序插入

    2.随机值

    6.AVL树的性能

    附录:


    1.AVL树

    1.1 AVL树的概念

    在上述我们所提到,二叉搜索树虽然可以缩短查找的效率,但如果数据有序或接近有序时,二叉搜索树将退化为单支树(单边树),查找元素相当于在顺序表中搜索元素,效率十分低下。因此,两位俄罗斯数学家G.M.Adelson-Velski和E.M.Landis 在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新节点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

    一颗AVL树或者空树具有一下性质:

    1. 它的左右子树都是AVL树
    2. 左右子树的高度之差(简称平衡因子)的绝对值不超过1(1/0/-1)

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

    此时我们大致了解了AVL树的平衡规则,接下来我们将手动模拟实现一下AVL树的主要功能。

    2.AVL树节点的定义

    AVL树节点的定义

    我们在此处实现的是KV模型的AVL树,K模型的较为简单,大家可以自己尝试实现。

    1. template<class K, class V>
    2. struct AVLTreeNode
    3. {
    4. pair _kv;
    5. AVLTreeNode* _left;
    6. AVLTreeNode* _right;
    7. AVLTreeNode* _parent;
    8. // 右子树-左子树 的高度差
    9. int _bf; // balance factor
    10. AVLTreeNode(const pair& kv)
    11. :_kv(kv)
    12. , _left(nullptr)
    13. , _right(nullptr)
    14. , _parent(nullptr)
    15. , _bf(0)
    16. {}
    17. // AVL树并没有规定必须要设计平衡因子
    18. // 只是一个实现的选择,方便控制平衡
    19. };

    从节点的定义我们可以看出,有普通的二叉搜树不同的是,AVL树中节点的设置添加了节点的parent节点,此处也是为了方便后续功能的实现(接着往下看就明白了)。除此之外,节点也定义了一个控制平衡因子_bf,用来表示当前节点右子树与左子树的高度差。

    3.AVL树的插入与旋转

    AVL树的插入就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看做一个二叉搜索树。

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

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

    AVL树在插入的过程中,如果插入新节点后导致AVL树中某些节点的平衡因子的绝对值大于等于1时,要进行旋转操作,根据节点插入位置的不同,AVL树的旋转分为四种。接下来我们逐一分析可能出现的情况与解决该问题的旋转方法。

    3.1 左单旋

    什么时候我们需要进行左单旋转呢?当新节点插入较高右子树的右侧时,我们看一个需要左单旋的最简单的情况。

    在下图中,当树要插入节点90,此时根节点的平衡因子为2,已经不满足AVL树的规则,因此要进行旋转。我们发现新节点插入了较高右子树的右侧。因此需要进行左单旋(即就是将右子树提上去)。

    左单旋的步骤:

    1.让插入节点的父节点,也就是这里的60的左子树变成节点30的右子树,30这颗子树成为60的左子树。

    2.调整改变节点的平衡因子。

    由于我们这里是最简单的情况,如果我们变成较为复杂的情况,如下图所示。

    在这个图中,无论在b还是c树下插入新节点,都是在30的右树下插入新节点,因此这里将有4中可能性(分别是b的左右孩子,c的左右孩子处),这里以c的孩子为例。但是情况均为右单旋情况。

    旋转步骤为:将60的左树变为30的右树,再让30成为60的左树,最后更改旋转因子。

    需要考虑的特殊情况:

    1. 60的节点的左孩子可能存在,也可能不存在。
    2. 30可能是根节点,也可能是子树:如果是根节点,旋转完成后,要更新根节点;如果是子树,可能是某个节点的左子树,也能是右子树。

    代码实现左单旋

    1. void RotateL(Node* parent)
    2. {
    3. //左旋
    4. Node* subR = parent->_right;
    5. Node* subRL = subR->_left;
    6. parent->_right = subRL;
    7. if (subRL)
    8. subRL->_parent = parent;
    9. Node* ppNode = parent->_parent;
    10. subR->_left = parent;
    11. parent->_parent = subR;
    12. if (parent == _root)
    13. {
    14. _root = subR;
    15. _root->_parent = nullptr;
    16. }
    17. else
    18. {
    19. if (parent == ppNode->_left)
    20. {
    21. ppNode->_left = subR;
    22. }
    23. else
    24. {
    25. ppNode->_right = subR;
    26. }
    27. subR->_parent = ppNode;
    28. }
    29. //更新平衡因子
    30. parent->_bf = 0;
    31. subR->_bf = 0;
    32. }

    3.2 右单旋

    右单旋是当新节点插入较高的左子树的左侧时进行使用。我们也来看一个最简单的右单旋的情况。

    在下图中,当我们要新插入节点10,我们发现30的左子树增加了一层,导致以60位根的二叉树不平衡,要让60平衡,只能将60的左子树的高度减少一层,右子树增加一层。即将左子树提上来。

    旋转步骤为:将30的右子树变成60的左子树,再让60变成30的左子树。旋转完成后更新节点的平衡因子即可。

    由于我们这里是最简单的情况,如果我们变成较为复杂的情况,如下图所示。

    右单旋需要考虑的情况:

    1. 30节点的右孩子可能存在也可能不存在
    2. 60可能是根节点,也可能是子树。如果是根节点,旋转完成后要更新跟节点。如果是子树,可能是某个节点的左子树也可能是右子树。

    代码实现右单旋

    1. void RotateR(Node* parent)
    2. {
    3. //右旋
    4. Node* subL = parent->_left;
    5. Node* subLR = subL->_right;
    6. parent->_left = subLR;
    7. if (subLR)
    8. subLR->_parent = parent;
    9. Node* ppNode = parent->_parent;
    10. subL->_right = parent;
    11. parent->_parent = subL;
    12. if (parent == _root)
    13. {
    14. _root = subL;
    15. _root->_parent = nullptr;
    16. }
    17. else
    18. {
    19. if (ppNode->_left == parent)
    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;
    30. }

    3.3 先左单旋再右单旋

    这是一种很复杂的情况,当新节点插入较高左子树的右侧时,要发生左右单旋。

    我们来看具体例子直观感受这种情况。将双旋变成单旋再旋转,即对30进行左单旋再对90进行右单旋,旋转完再考虑平衡因子的更新。

    左右双旋代码实现:

    1. //左右双旋
    2. void RotateLR(Node* parent)
    3. {
    4. Node* subL = parent->_left;
    5. Node* subLR = subL->_right;
    6. int bf = subLR->_bf;
    7. RotateL(parent->_left);//左
    8. RotateR(parent);//右
    9. //更新平衡因子
    10. if (bf == 0)
    11. {
    12. parent->_bf = 0;
    13. subL->_bf = 0;
    14. subLR->_bf = 0;
    15. }
    16. else if (bf == 1)
    17. {
    18. parent->_bf = 0;
    19. subL->_bf = -1;
    20. subLR->_bf = 0;
    21. }
    22. else if (bf == -1)
    23. {
    24. parent->_bf = 1;
    25. subL->_bf = 0;
    26. subLR->_bf = 0;
    27. }
    28. else
    29. {
    30. // subLR->_bf 旋转前就出现问题
    31. assert(false);
    32. }
    33. }

    3.4 先右单旋再左单旋

    当新节点插入较高右子树的左侧时就要进行右左单旋。我们直接来看具体情况

    将右左双旋变成单旋再旋转,即对90进行右单旋再对30进行左单旋,旋转完再考虑平衡因子的更新。

    右左双旋代码实现

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

    AVL树的旋转共以上四种情况。

    总结:

    加入pParent为根的子树不平衡是,即pParent的平衡因子为2或者-2,分以下情况考虑

    1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根伟pSubR
    • 当pSubR的平衡因子为1时,执行左单旋
    • 当pSubR的平衡因子为-1时,执行右左双旋转
    1. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的跟为pSubL
    • 当pSubL的平衡因子为-1时,执行右单旋
    • 当pSubL的平衡因子为1是,执行左右双旋

    旋转完成后,原pParent为跟的子树的高度降低,已经平衡,不需要再向上更新了。

    考虑完所有的旋转情况后,我们此时实现insert插入。

    insert插入大框架还是二叉搜索树的insert,只不过加入了平衡因子,不平衡时进行旋转。

    Insert实现代码

    1. bool Insert(const pair& kv)
    2. {
    3. //1、按照搜索树的规则插入
    4. //2、看是否违反平衡规则,如果违反就需要处理:旋转
    5. if (_root == nullptr)
    6. {
    7. _root = new Node(kv);
    8. _root->_bf = 0;
    9. return true;
    10. }
    11. Node* parent = nullptr;
    12. Node* cur = _root;
    13. while (cur)
    14. {
    15. if (cur->_kv.first < kv.first)
    16. {
    17. parent = cur;
    18. cur = cur->_right;
    19. }
    20. else if (cur->_kv.first > kv.first)
    21. {
    22. parent = cur;
    23. cur = cur->_left;
    24. }
    25. else
    26. {
    27. return false;
    28. }
    29. }
    30. cur = new Node(kv);
    31. if (parent->_kv.first < kv.first)
    32. {
    33. parent->_right = cur;
    34. }
    35. else
    36. {
    37. parent->_left = cur;
    38. }
    39. cur->_parent = parent;
    40. //.....
    41. //更新平衡因子
    42. while (parent)
    43. {
    44. if (cur == parent->_right)
    45. {
    46. parent->_bf++;
    47. }
    48. else
    49. {
    50. parent->_bf--;
    51. }
    52. //是否继续更新?
    53. // 1 or -1 ->插入节点填上了矮的那边
    54. if (parent->_bf == 0)
    55. {
    56. break;
    57. }
    58. //0->1 或者 -1 插入节点导致一边变高了
    59. else if (parent->_bf == 1 || parent->_bf == -1)
    60. {
    61. //子树的高度变了,继续更新祖先
    62. cur = cur->_parent;
    63. parent = parent->_parent;
    64. }
    65. // -1 or 1 -》 2 or -2 插入几点导致本来高的一边更高了
    66. else if (parent->_bf == 2 || parent->_bf == -2)
    67. {
    68. //子树不平衡-- 需要旋转处理
    69. if (parent->_bf == 2 && cur->_bf == 1)//左单旋
    70. {
    71. RotateL(parent);
    72. }
    73. else if (parent->_bf == -2 && cur->_bf == -1)//右
    74. {
    75. RotateR(parent);
    76. }
    77. else if (parent->_bf == -2 && cur->_bf == -1)//左右双旋
    78. {
    79. RotateLR(parent);
    80. }
    81. else if (parent->_bf == 2 && cur->_bf == -1) // 右左双旋
    82. {
    83. RotateRL(parent);
    84. }
    85. break;
    86. }
    87. else
    88. {
    89. //插入之前AVL树就存在不平衡子树
    90. assert(false);
    91. }
    92. }
    93. return true;
    94. }

    4.判断一棵树是否是AVL树

    由于AVL是在二叉搜索树的基础上加入了平衡机制,因此验证AVL树可以分为两步:

    1. 首先验证其是否为二叉搜索树

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

    1. 验证其是否为平衡树(把握AVL树的规则)
      1. 每个节点子树高度差的绝对值不超过1(注意节点如果没有平衡因子)
      2. 节点的平衡因子是否计算正确
      3. 每个AVL树的左右子树也为AVL树,因此可以使用递归判断其左右子树是否为AVL树

    验证AVL树代码实现:

    1. void _InOrder(Node* root)
    2. {
    3. if (root == nullptr)
    4. return;
    5. _InOrder(root->_left);
    6. cout << root->_kv.first << " ";
    7. _InOrder(root->_right);
    8. }
    9. int _Height(Node* root)
    10. {
    11. if (root == nullptr)
    12. return 0;
    13. int lh = _Height(root->_left);
    14. int rh = _Height(root->_right);
    15. return lh > rh ? lh + 1 : rh + 1;
    16. }
    17. //是否是平衡树
    18. bool _IsBalanceTree(Node* root)
    19. {
    20. // 空树也是AVL树
    21. if (nullptr == root)
    22. return true;
    23. // 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
    24. int leftHeight = _Height(root->_left);
    25. int rightHeight = _Height(root->_right);
    26. int diff = rightHeight - leftHeight;
    27. // 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
    28. // pRoot平衡因子的绝对值超过1,则一定不是AVL树
    29. if (abs(diff) >= 2)
    30. {
    31. cout << root->_kv.first << "节点平衡因子异常" << endl;
    32. return false;
    33. }
    34. if (diff != root->_bf)
    35. {
    36. cout << root->_kv.first << "节点平衡因子不符合实际" << endl;
    37. return false;
    38. }
    39. // pRoot的左和右如果都是AVL树,则该树一定是AVL树
    40. return _IsBalanceTree(root->_left)
    41. && _IsBalanceTree(root->_right);
    42. }

    5.AVL树的验证与查看

    我们使用层序遍历的方式输出打印可以直观查看AVL树。使用层序遍历需要借助队列。

    1. //层序输出
    2. vectorint>> levelOrder() {
    3. vectorint>> vv;
    4. if (_root == nullptr)
    5. return vv;
    6. queue q;
    7. int levelSize = 1;
    8. q.push(_root);
    9. while (!q.empty())
    10. {
    11. // levelSize控制一层一层出
    12. vector<int> levelV;
    13. while (levelSize--)
    14. {
    15. Node* front = q.front();
    16. q.pop();
    17. levelV.push_back(front->_kv.first);
    18. if (front->_left)
    19. q.push(front->_left);
    20. if (front->_right)
    21. q.push(front->_right);
    22. }
    23. vv.push_back(levelV);
    24. for (auto e : levelV)
    25. {
    26. cout << e << " ";
    27. }
    28. cout << endl;
    29. // 上一层出完,下一层就都进队列
    30. levelSize = q.size();
    31. }
    32. return vv;
    33. }

    验证与查看:

    我们手动模拟一些值,让其插入输出。

    1.顺序插入

    1. void TestAVLTree2()
    2. {
    3. const size_t N = 10;
    4. vector<int> v;
    5. v.reserve(N);
    6. srand(time(0));//生成随机树
    7. for (size_t i = 0; i < N; ++i)
    8. {
    9. //v.push_back(rand());
    10. v.push_back(i);
    11. }
    12. AVLTree<int, int> t;
    13. for (auto e : v)
    14. {
    15. t.Insert(make_pair(e, e));
    16. }
    17. t.levelOrder();
    18. cout << endl;
    19. cout << "是否平衡?" << t.IsBalanceTree() << endl;
    20. cout << "高度:" << t.Height() << endl;
    21. //t.InOrder();
    22. }

    2.随机值

    6.AVL树的性能

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

    (本篇完)

    附录:

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. using namespace std;
    10. template<class K, class V>
    11. struct AVLTreeNode
    12. {
    13. pair _kv;
    14. AVLTreeNode* _left;
    15. AVLTreeNode* _right;
    16. AVLTreeNode* _parent;
    17. // 右子树-左子树 的高度差
    18. int _bf; // balance factor
    19. AVLTreeNode(const pair& kv)
    20. :_kv(kv)
    21. , _left(nullptr)
    22. , _right(nullptr)
    23. , _parent(nullptr)
    24. , _bf(0)
    25. {}
    26. // AVL树并没有规定必须要设计平衡因子
    27. // 只是一个实现的选择,方便控制平衡
    28. };
    29. template<class K, class V>
    30. class AVLTree
    31. {
    32. typedef AVLTreeNode Node;
    33. public:
    34. bool Insert(const pair& kv)
    35. {
    36. //1、按照搜索树的规则插入
    37. //2、看是否违反平衡规则,如果违反就需要处理:旋转
    38. if (_root == nullptr)
    39. {
    40. _root = new Node(kv);
    41. _root->_bf = 0;
    42. return true;
    43. }
    44. Node* parent = nullptr;
    45. Node* cur = _root;
    46. while (cur)
    47. {
    48. if (cur->_kv.first < kv.first)
    49. {
    50. parent = cur;
    51. cur = cur->_right;
    52. }
    53. else if (cur->_kv.first > kv.first)
    54. {
    55. parent = cur;
    56. cur = cur->_left;
    57. }
    58. else
    59. {
    60. return false;
    61. }
    62. }
    63. cur = new Node(kv);
    64. if (parent->_kv.first < kv.first)
    65. {
    66. parent->_right = cur;
    67. }
    68. else
    69. {
    70. parent->_left = cur;
    71. }
    72. cur->_parent = parent;
    73. //.....
    74. //更新平衡因子
    75. while (parent)
    76. {
    77. if (cur == parent->_right)
    78. {
    79. parent->_bf++;
    80. }
    81. else
    82. {
    83. parent->_bf--;
    84. }
    85. //是否继续更新?
    86. // 1 or -1 ->插入节点填上了矮的那边
    87. if (parent->_bf == 0)
    88. {
    89. break;
    90. }
    91. //0->1 或者 -1 插入节点导致一边变高了
    92. else if (parent->_bf == 1 || parent->_bf == -1)
    93. {
    94. //子树的高度变了,继续更新祖先
    95. cur = cur->_parent;
    96. parent = parent->_parent;
    97. }
    98. // -1 or 1 -》 2 or -2 插入几点导致本来高的一边更高了
    99. else if (parent->_bf == 2 || parent->_bf == -2)
    100. {
    101. //子树不平衡-- 需要旋转处理
    102. if (parent->_bf == 2 && cur->_bf == 1)//左单旋
    103. {
    104. RotateL(parent);
    105. }
    106. else if (parent->_bf == -2 && cur->_bf == -1)//右
    107. {
    108. RotateR(parent);
    109. }
    110. else if (parent->_bf == -2 && cur->_bf == -1)//左右双旋
    111. {
    112. RotateLR(parent);
    113. }
    114. else if (parent->_bf == 2 && cur->_bf == -1) // 右左双旋
    115. {
    116. RotateRL(parent);
    117. }
    118. break;
    119. }
    120. else
    121. {
    122. //插入之前AVL树就存在不平衡子树
    123. assert(false);
    124. }
    125. }
    126. return true;
    127. }
    128. private:
    129. void RotateL(Node* parent)
    130. {
    131. //左旋
    132. Node* subR = parent->_right;
    133. Node* subRL = subR->_left;
    134. parent->_right = subRL;
    135. if (subRL)
    136. subRL->_parent = parent;
    137. Node* ppNode = parent->_parent;
    138. subR->_left = parent;
    139. parent->_parent = subR;
    140. if (parent == _root)
    141. {
    142. _root = subR;
    143. _root->_parent = nullptr;
    144. }
    145. else
    146. {
    147. if (parent == ppNode->_left)
    148. {
    149. ppNode->_left = subR;
    150. }
    151. else
    152. {
    153. ppNode->_right = subR;
    154. }
    155. subR->_parent = ppNode;
    156. }
    157. //更新平衡因子
    158. parent->_bf = 0;
    159. subR->_bf = 0;
    160. }
    161. void RotateR(Node* parent)
    162. {
    163. //右旋
    164. Node* subL = parent->_left;
    165. Node* subLR = subL->_right;
    166. parent->_left = subLR;
    167. if (subLR)
    168. subLR->_parent = parent;
    169. Node* ppNode = parent->_parent;
    170. subL->_right = parent;
    171. parent->_parent = subL;
    172. if (parent == _root)
    173. {
    174. _root = subL;
    175. _root->_parent = nullptr;
    176. }
    177. else
    178. {
    179. if (ppNode->_left == parent)
    180. {
    181. ppNode->_left = subL;
    182. }
    183. else
    184. {
    185. ppNode->_right = subL;
    186. }
    187. subL->_parent = ppNode;
    188. }
    189. subL->_bf = parent->_bf = 0;
    190. }
    191. //左右双旋
    192. void RotateLR(Node* parent)
    193. {
    194. Node* subL = parent->_left;
    195. Node* subLR = subL->_right;
    196. int bf = subLR->_bf;
    197. RotateL(parent->_left);//左
    198. RotateR(parent);//右
    199. //更新平衡因子
    200. if (bf == 0)
    201. {
    202. parent->_bf = 0;
    203. subL->_bf = 0;
    204. subLR->_bf = 0;
    205. }
    206. else if (bf == 1)
    207. {
    208. parent->_bf = 0;
    209. subL->_bf = -1;
    210. subLR->_bf = 0;
    211. }
    212. else if (bf == -1)
    213. {
    214. parent->_bf = 1;
    215. subL->_bf = 0;
    216. subLR->_bf = 0;
    217. }
    218. else
    219. {
    220. // subLR->_bf 旋转前就出现问题
    221. assert(false);
    222. }
    223. }
    224. //右左双旋
    225. void RotateRL(Node* parent)
    226. {
    227. Node* subR = parent->_right;
    228. Node* subRL = subR->_left;
    229. int bf = subRL->_bf;
    230. RotateR(parent->_right);
    231. RotateL(parent);
    232. if (bf == 0)
    233. {
    234. subRL->_bf = 0;
    235. parent->_bf = 0;
    236. subR->_bf = 0;
    237. }
    238. else if (bf == 1)
    239. {
    240. subRL->_bf = 0;
    241. parent->_bf = -1;
    242. subR->_bf = 0;
    243. }
    244. else if(bf == -1)
    245. {
    246. subRL->_bf = 0;
    247. parent->_bf = 0;
    248. subR->_bf = 1;
    249. }
    250. else
    251. {
    252. // subLR->_bf 旋转前就有问题
    253. assert(false);
    254. }
    255. }
    256. void _InOrder(Node* root)
    257. {
    258. if (root == nullptr)
    259. return;
    260. _InOrder(root->_left);
    261. cout << root->_kv.first << " ";
    262. _InOrder(root->_right);
    263. }
    264. int _Height(Node* root)
    265. {
    266. if (root == nullptr)
    267. return 0;
    268. int lh = _Height(root->_left);
    269. int rh = _Height(root->_right);
    270. return lh > rh ? lh + 1 : rh + 1;
    271. }
    272. //是否是平衡树
    273. bool _IsBalanceTree(Node* root)
    274. {
    275. // 空树也是AVL树
    276. if (nullptr == root)
    277. return true;
    278. // 计算pRoot节点的平衡因子:即pRoot左右子树的高度差
    279. int leftHeight = _Height(root->_left);
    280. int rightHeight = _Height(root->_right);
    281. int diff = rightHeight - leftHeight;
    282. // 如果计算出的平衡因子与pRoot的平衡因子不相等,或者
    283. // pRoot平衡因子的绝对值超过1,则一定不是AVL树
    284. if (abs(diff) >= 2)
    285. {
    286. cout << root->_kv.first << "节点平衡因子异常" << endl;
    287. return false;
    288. }
    289. if (diff != root->_bf)
    290. {
    291. cout << root->_kv.first << "节点平衡因子不符合实际" << endl;
    292. return false;
    293. }
    294. // pRoot的左和右如果都是AVL树,则该树一定是AVL树
    295. return _IsBalanceTree(root->_left)
    296. && _IsBalanceTree(root->_right);
    297. }
    298. public:
    299. void InOrder()
    300. {
    301. _InOrder(_root);
    302. cout << endl;
    303. }
    304. bool IsBalanceTree()
    305. {
    306. return _IsBalanceTree(_root);
    307. }
    308. int Height()
    309. {
    310. return _Height(_root);
    311. }
    312. //层序输出
    313. vectorint>> levelOrder() {
    314. vectorint>> vv;
    315. if (_root == nullptr)
    316. return vv;
    317. queue q;
    318. int levelSize = 1;
    319. q.push(_root);
    320. while (!q.empty())
    321. {
    322. // levelSize控制一层一层出
    323. vector<int> levelV;
    324. while (levelSize--)
    325. {
    326. Node* front = q.front();
    327. q.pop();
    328. levelV.push_back(front->_kv.first);
    329. if (front->_left)
    330. q.push(front->_left);
    331. if (front->_right)
    332. q.push(front->_right);
    333. }
    334. vv.push_back(levelV);
    335. for (auto e : levelV)
    336. {
    337. cout << e << " ";
    338. }
    339. cout << endl;
    340. // 上一层出完,下一层就都进队列
    341. levelSize = q.size();
    342. }
    343. return vv;
    344. }
    345. private:
    346. Node* _root = nullptr;
    347. };
    348. void TestAVLTree()
    349. {
    350. AVLTree<int, int> t;
    351. t.Insert(make_pair(1, 1));
    352. t.Insert(make_pair(2, 2));
    353. t.Insert(make_pair(3, 3));
    354. }
    355. void TestAVLTree1()
    356. {
    357. int a[] = { 30,29,28,27,26,25,24,11,8,7,6,5,4,3,2,1 };
    358. //int a[] = { 30,1,3,15,33,14,2,7,8,20,27,25 };
    359. AVLTree<int, int> t;
    360. for (auto e : a)
    361. {
    362. t.Insert(make_pair(e, e));
    363. }
    364. t.levelOrder();
    365. }
    366. void TestAVLTree2()
    367. {
    368. const size_t N = 10;
    369. vector<int> v;
    370. v.reserve(N);
    371. srand(time(0));//生成随机树
    372. for (size_t i = 0; i < N; ++i)
    373. {
    374. v.push_back(rand());
    375. //v.push_back(i);
    376. }
    377. AVLTree<int, int> t;
    378. for (auto e : v)
    379. {
    380. t.Insert(make_pair(e, e));
    381. }
    382. t.levelOrder();
    383. cout << endl;
    384. cout << "是否平衡?" << t.IsBalanceTree() << endl;
    385. cout << "高度:" << t.Height() << endl;
    386. //t.InOrder();
    387. }

  • 相关阅读:
    Qt的Pro文件
    Android: HttpURLConnection获取JSON数据
    防火墙(Firewall)
    PXE高效批量网络装机
    最常用的 9 个JavaScript 函数与示例
    转行网络安全是否可行?
    性能测试 —— 生成html测试报告、参数化、jvm监控
    Session详解
    三西格玛和六西格玛区别是什么?优思学院用一幅图告诉你
    element表格table点击添加背景色
  • 原文地址:https://blog.csdn.net/qq_58325487/article/details/127057530