当前位置:   article > 正文

数据结构中的平衡搜索树 --- AVL树是怎样进行旋转处理的?(平衡因子版本)

数据结构中的平衡搜索树 --- AVL树是怎样进行旋转处理的?(平衡因子版本)

目录

前言

        搜索二叉树

AVL树

节点的定义

插入

旋转


前言

搜索二叉树

搜索二叉树 又称 二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

示例:

具体可以查看搜索二叉树

         但是二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树(或接近单只树),二叉搜索树的性能就失去了,并且 时间复杂度会退化成O(N),因此需要对底层结构进行平衡处理,即采用平衡树(AVL树、红黑树)来实现,使二叉搜索树的性能都能达到最优.

AVL树

AVL树的概念

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

一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
  • 它的左右子树都是AVL
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1) --- 一般是右子树减去左子树等于根

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

AVL树节点的定义
  1. template<class K, class V>
  2. class 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. };
AVL树的插入
AVL 树就是在二叉搜索树的基础上引入了平衡因子,因此 AVL 树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:
  • 按照二叉搜索树的方式插入新节点
  •  调整节点的平衡因子
  1. template<class K,class V>
  2. class AVLTree
  3. {
  4. typedef AVLTreeNode<K, V> Node;
  5. public:
  6. bool insert(const pair<K, V>& kv)
  7. {
  8. //_root为空
  9. if (_root == nullptr)
  10. {
  11. _root = new Node(kv);
  12. return true;
  13. }
  14. //_root不为空
  15. Node* cur = _root;
  16. Node* parent = nullptr; //记录cur的父节点,方便进行链接
  17. while (cur)
  18. {
  19. if (kv.first < cur->_kv.first) //插入的值小于存储的值
  20. {
  21. parent = cur;
  22. cur = cur->_left;
  23. }
  24. else if(kv.first > cur->_kv.first) //插入的值大于存储的值
  25. {
  26. parent = cur;
  27. cur = cur->_right;
  28. }
  29. else
  30. {
  31. return false; //相等,则插入失败
  32. }
  33. }
  34. //当前位置为空,插入的值与原本的值不相等,进行链接
  35. cur = new Node(kv);
  36. if (kv.first < parent->_kv.first)
  37. {
  38. parent->_left = cur;
  39. }
  40. else
  41. {
  42. parent->_right = cur;
  43. }
  44. cur->_parent = parent; //需要注意,进行链接
  45. //链接之后,此时需要更新平衡因子
  46. //.......
  47. return true;
  48. }
  49. private:
  50. Node* _root = nullptr;
  51. };

此时怎样调整节点的平衡因子呢?

观察一下平衡因子的性质: 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

                                                                        而且平时我们习惯使用右子树高度减去左子树高度等于根

可以得出:如果新增节点是右子树,那么父节点需要++;如果新增节点是左子树,那么父节点需要 --

cur = parent->_right;     parent->_bf++;

cur = parent->_left;       parent->_bf--;

示例1:

示例2:

那么此时产生的新问题是,当父节点更新后,要不要继续向上更新?或者什么决定了要不要继续向上更新???

观察示例1与示例2可以得出,如果parent节点的高度发生了变化,那么是需要继续更新的,如果parent的高度没有发生变化,那么就不需要继续更新。

  • 情况1:parent->_bf == 1 || parent->_bf == -1      ---    需要继续向上更新,因为说明插入之前 parent->_bf == 0  ,表示插入之前父节点两边的高度相等,现在有一边插入了一个新节点,此时高度发生了改变,所以需要继续向上更新

  • 情况2:parent->_bf == 0     ---    不用向上更新,因为说明插入之前 parent->_bf == 1 || parent->_bf == -1,表示插入之前父节点两边的高度不相等,现在矮的一边插入了一个新节点,此时高度平衡,所以不用向上更新。

  • 情况3:parent->_bf == 2 || parent->_bf == -2   ---   所在子树高度不平衡,需要进行旋转处理
  1. template<class K,class V>
  2. class AVLTree
  3. {
  4. typedef AVLTreeNode<K, V> Node;
  5. public:
  6. bool insert(const pair<K, V>& kv)
  7. {
  8. //_root为空
  9. if (_root == nullptr)
  10. {
  11. _root = new Node(kv);
  12. return true;
  13. }
  14. //_root不为空
  15. Node* cur = _root;
  16. Node* parent = nullptr; //记录cur的父节点,方便进行链接
  17. while (cur)
  18. {
  19. if (kv.first < cur->_kv.first) //插入的值小于存储的值
  20. {
  21. parent = cur;
  22. cur = cur->_left;
  23. }
  24. else if(kv.first > cur->_kv.first) //插入的值大于存储的值
  25. {
  26. parent = cur;
  27. cur = cur->_right;
  28. }
  29. else
  30. {
  31. return false; //相等,则插入失败
  32. }
  33. }
  34. //当前位置为空,插入的值与原本的值不相等,进行链接
  35. cur = new Node(kv);
  36. if (kv.first < parent->_kv.first)
  37. {
  38. parent->_left = cur;
  39. }
  40. else
  41. {
  42. parent->_right = cur;
  43. }
  44. cur->_parent = parent; //需要注意,进行链接
  45. //链接之后,此时需要更新平衡因子
  46. while (parent)
  47. {
  48. if (cur == parent->_right)
  49. {
  50. parent->_bf++;
  51. }
  52. else
  53. {
  54. parent->_bf--;
  55. }
  56. if (parent->_bf == 0)
  57. {
  58. break;
  59. }
  60. else if (parent->_bf == 1 || parent->_bf == -1)
  61. {
  62. //继续向上更新
  63. parent = parent->_parent;
  64. cur = cur->_parent;
  65. }
  66. else if (parent->_bf == 2 || parent->_bf == -2)
  67. {
  68. //需要进行旋转处理
  69. //........
  70. }
  71. else
  72. {
  73. //程序走到这里说明问题很严重,直接断言
  74. assert(false);
  75. }
  76. }
  77. return true;
  78. }
  79. private:
  80. Node* _root = nullptr;
  81. };

那么什么情况下会出现旋转处理???

1.更新平衡因子:如果更新完成,平衡因子没有出现问题 | _bf l <= 1,平衡结构没有受到影响,不需要处理
 

2.更新平衡因子:如果更新完成,平衡因子出现问题 | _bf | > 1,平衡结构受到影响,需要处理(旋转)

AVL树的旋转

   如果在一棵原本是平衡的AVL 树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。所以旋转的本质有两点:
  • 1.让这棵子树平衡
  • 2.降低这棵子树的高度
根据节点插入位置的不同,AVL 树的旋转分为四种:

1.新节点插入较高右子树的右侧--- 右右:左单旋
抽象图:

旋转的过程:
  • ① b变成了30的右边
  • ② 30变成60的左边
  • ③ 60变成整棵树的根
     
在旋转过程中,有以下几种情况需要考虑:
  • ① 60节点的左孩子可能存在,也可能不存在
  • ② 30可能是根节点,也可能是子树

如果是根节点,旋转完成后,要更新根节点

 如果是子树,可能是某个节点的左子树,也可能是右子树

具象图:

当h == 0:

当h == 1:

当h == 2:
 

示例:

变量定义:

代码:
  1. //左单旋
  2. void RotateL(Node* parent)
  3. {
  4. Node* subR = parent->_right;
  5. Node* subRL = subR->_left;
  6. parent->_right = subRL;
  7. if (subRL)
  8. subRL->_parent = parent;
  9. //提前记录祖先节点
  10. Node* pparent = parent->_parent;
  11. subR->_left = parent;
  12. parent->_parent = subR;
  13. //值得注意的是,parent节点不一定为根节点,也就是旋转的可能是一棵子树而不是整棵树
  14. if (pparent == nullptr) //意味着parent节点是根节点
  15. {
  16. _root = subR;
  17. _root->_parent = nullptr;
  18. }
  19. else
  20. {
  21. //判断parent 在 祖先节点的左还是右
  22. if (pparent->_right == parent)
  23. {
  24. pparent->_right = subR;
  25. }
  26. else
  27. {
  28. pparent->_left = subR;
  29. }
  30. subR->_parent = pparent; //更改subR的父节点
  31. }
  32. //注意:一定要更新平衡因子
  33. parent->_bf = subR->_bf = 0;
  34. }

2.新节点插入较高左子树的左侧 -- 左左:右单旋 (可参考左单旋)
抽象图:

代码:

  1. //右单旋
  2. void RotateR(Node* parent)
  3. {
  4. Node* subL = parent->_left;
  5. Node* subLR = subL->_right;
  6. parent->_left = subLR;
  7. if (subLR)
  8. subLR->_parent = parent;
  9. //提前记录祖先节点
  10. Node* pparent = parent->_parent;
  11. subL->_right = parent;
  12. parent->_parent = subL;
  13. if (parent == _root)
  14. {
  15. _root = subL;
  16. _root->_parent = nullptr;
  17. }
  18. else
  19. {
  20. //判断parent 在 祖先节点的左还是右
  21. if (pparent->_right == parent)
  22. {
  23. pparent->_right = subL;
  24. }
  25. else
  26. {
  27. pparent->_left = subL;
  28. }
  29. subL->_parent = pparent; //更改subR的父节点
  30. }
  31. //注意:一定要更新平衡因子
  32. parent->_bf = subL->_bf = 0;
  33. }

3. 新节点插入较高左子树的右侧 --- 左右:先左单旋再右单旋
抽象图:
代码演示:
  1. void RotateLR(Node* parent)
  2. {
  3. RotateL(parent->_left); //左旋
  4. RotateR(parent); //右旋
  5. }

这样可以嘛?其实有个非常严重的错误,就是无论左旋还是右旋函数最后都会把parent ,subR,subL的平衡因子置成0

        以上面的图为例(新增节点在subLR的左子树):第一次单旋会把30节点 、60节点 的平衡因子置成 0,第二次单旋会把60节点 、90节点 的平衡因子置成 0 ,这显然是不对的,因为90节点最后的平衡因子应该是1。所以需要分情况讨论:

新增节点在subLR的右子树

一般来说最后一种不需要考虑,因为都会被单旋修改为0,但是建议不要依赖单旋

总结这里不能依靠左旋or右旋函数来修改平衡因子,需要手动修改

代码如下:

  1. //左右双旋
  2. void RotateLR(Node* parent)
  3. {
  4. Node* subL = parent->_left;
  5. Node* subLR = subL->_right;
  6. //因为双旋过后 _bf 都会被修改为0,所以需要提前记录
  7. int bf = subLR->_bf;
  8. RotateL(parent->_left); //左旋
  9. RotateR(parent); //右旋
  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 = 1;
  19. subL->_bf = 0;
  20. subLR->_bf = 0;
  21. }
  22. else if (bf == 1)
  23. {
  24. parent->_bf = 0;
  25. subL->_bf = -1;
  26. subLR->_bf = 0;
  27. }
  28. else
  29. {
  30. assert(false); //依旧直接断言,走到这里说明程序出现严重错误
  31. }
  32. }

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

(参考左右双旋)

抽象图:

代码:

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

那么什么时候左旋?什么时候右旋,什么时候双旋呢???

观察上面4种旋转的情况可以知道:

  • 左子树高 - 右旋 
  • 右子树高 - 左旋   
  • 左子树高,左子树的右孩子高 - 左右双旋
  • 右子树高,右子树的左孩子高 - 右左双旋

 AVL 树的删除
        因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将节点删除,然后再更新平衡因子,只不错与删除不同的时,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。
AVL 树的性能
        AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过 1 ,这样可以保证查询时高效的时间复杂度,即$log_2 (N)$ 。但是如果要对 AVL 树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的( 即不会改变 ) ,可以考虑 AVL 树,但一个结构经常修改,就不太适合。

最后附上完整代码以及测试一棵树是否是AVL树的方法:

                                                                                                                AVLTree.h

  1. #pragma once
  2. #include <iostream>
  3. #include <assert.h>
  4. #include <string>
  5. using namespace std;
  6. template<class K, class V>
  7. struct AVLTreeNode
  8. {
  9. AVLTreeNode<K,V>* _left; //左子树节点
  10. AVLTreeNode<K, V>* _right; //右子树节点
  11. AVLTreeNode<K, V>* _parent; //父节点
  12. pair<K, V> _kv;
  13. int _bf; //balance factor :平衡因子
  14. AVLTreeNode(const pair<K, V>& kv)
  15. :_left(nullptr)
  16. , _right(nullptr)
  17. , _parent(nullptr)
  18. ,_kv(kv)
  19. ,_bf(0)
  20. {}
  21. };
  22. template<class K,class V>
  23. class AVLTree
  24. {
  25. typedef AVLTreeNode<K, V> Node;
  26. public:
  27. void InOrder()
  28. {
  29. _Inorder(_root);
  30. cout << endl;
  31. }
  32. bool Insert(const pair<K, V>& kv)
  33. {
  34. //_root为空
  35. if (_root == nullptr)
  36. {
  37. _root = new Node(kv);
  38. return true;
  39. }
  40. //_root不为空
  41. Node* cur = _root;
  42. Node* parent = nullptr; //记录cur的父节点,方便进行链接
  43. while (cur)
  44. {
  45. if (kv.first < cur->_kv.first) //插入的值小于存储的值
  46. {
  47. parent = cur;
  48. cur = cur->_left;
  49. }
  50. else if (kv.first > cur->_kv.first) //插入的值大于存储的值
  51. {
  52. parent = cur;
  53. cur = cur->_right;
  54. }
  55. else
  56. {
  57. return false; //相等,则插入失败
  58. }
  59. }
  60. //当前位置为空,插入的值与原本的值不相等,进行链接
  61. cur = new Node(kv);
  62. if (kv.first < parent->_kv.first)
  63. {
  64. parent->_left = cur;
  65. }
  66. else
  67. {
  68. parent->_right = cur;
  69. }
  70. cur->_parent = parent; //需要注意,进行链接
  71. //链接之后,此时需要更新平衡因子
  72. while (parent)
  73. {
  74. if (cur == parent->_right)
  75. {
  76. parent->_bf++;
  77. }
  78. else
  79. {
  80. parent->_bf--;
  81. }
  82. if (parent->_bf == 0)
  83. {
  84. break;
  85. }
  86. else if (parent->_bf == 1 || parent->_bf == -1)
  87. {
  88. //继续向上更新
  89. parent = parent->_parent;
  90. cur = cur->_parent;
  91. }
  92. else if (parent->_bf == 2 || parent->_bf == -2)
  93. {
  94. //需要进行旋转处理 --- 1.降低子树的高度 2.继续保持平衡
  95. if (parent->_bf == 2 && cur->_bf == 1)
  96. {
  97. //左旋
  98. RotateL(parent);
  99. }
  100. else if (parent->_bf == -2 && cur->_bf == -1)
  101. {
  102. //右旋
  103. RotateR(parent);
  104. }
  105. else if (parent->_bf == -2 && cur->_bf == 1)
  106. {
  107. //左右双旋 - 根的左子树高 左子树的右子树高
  108. RotateLR(parent);
  109. }
  110. else if (parent->_bf == 2 && cur->_bf == -1)
  111. {
  112. //右左双旋 - 根的右子树高 右子树的左子树高
  113. RotateRL(parent);
  114. }
  115. else
  116. {
  117. assert(false);
  118. }
  119. break; //旋转之后是可以直接跳出循环的,旋转之后(不管是整棵树还是子树)都是平衡的
  120. }
  121. else
  122. {
  123. //程序走到这里说明问题很严重,直接断言
  124. assert(false);
  125. }
  126. }
  127. return true;
  128. }
  129. //判断是否为AVL树
  130. bool IsBalance()
  131. {
  132. return _IsBalance(_root);
  133. }
  134. protected:
  135. void _Inorder(Node* root)
  136. {
  137. if (root == nullptr)
  138. return;
  139. _Inorder(root->_left);
  140. cout << root->_kv.first << " ";
  141. _Inorder(root->_right);
  142. }
  143. //左单旋
  144. void RotateL(Node* parent)
  145. {
  146. Node* subR = parent->_right;
  147. Node* subRL = subR->_left;
  148. parent->_right = subRL;
  149. if (subRL)
  150. subRL->_parent = parent;
  151. //提前记录祖先节点
  152. Node* pparent = parent->_parent;
  153. subR->_left = parent;
  154. parent->_parent = subR;
  155. //值得注意的是,parent节点不一定为根节点,也就是旋转的可能是一棵子树而不是整棵树
  156. if (pparent == nullptr) //意味着parent节点是根节点
  157. {
  158. _root = subR;
  159. _root->_parent = nullptr;
  160. }
  161. else
  162. {
  163. //判断parent 在 祖先节点的左还是右
  164. if (pparent->_right == parent)
  165. {
  166. pparent->_right = subR;
  167. }
  168. else
  169. {
  170. pparent->_left = subR;
  171. }
  172. subR->_parent = pparent; //更改subR的父节点
  173. }
  174. //注意:一定要更新平衡因子
  175. parent->_bf = subR->_bf = 0;
  176. }
  177. //右单旋
  178. void RotateR(Node* parent)
  179. {
  180. Node* subL = parent->_left;
  181. Node* subLR = subL->_right;
  182. parent->_left = subLR;
  183. if (subLR)
  184. subLR->_parent = parent;
  185. //提前记录祖先节点
  186. Node* pparent = parent->_parent;
  187. subL->_right = parent;
  188. parent->_parent = subL;
  189. if (parent == _root)
  190. {
  191. _root = subL;
  192. _root->_parent = nullptr;
  193. }
  194. else
  195. {
  196. //判断parent 在 祖先节点的左还是右
  197. if (pparent->_right == parent)
  198. {
  199. pparent->_right = subL;
  200. }
  201. else
  202. {
  203. pparent->_left = subL;
  204. }
  205. subL->_parent = pparent; //更改subR的父节点
  206. }
  207. //注意:一定要更新平衡因子
  208. parent->_bf = subL->_bf = 0;
  209. }
  210. //左右双旋
  211. void RotateLR(Node* parent)
  212. {
  213. //记录修改平衡因子的位置
  214. Node* subL = parent->_left;
  215. Node* subLR = subL->_right;
  216. //因为双旋过后bf都会被修改为0,所以需要提前记录subLR的平衡因子
  217. int bf = subLR->_bf;
  218. RotateL(parent->_left);
  219. RotateR(parent);
  220. //分三种情况
  221. if (bf == 1)
  222. {
  223. parent->_bf = 0;
  224. subL->_bf = -1;
  225. subLR->_bf = 0;
  226. }
  227. else if (bf == -1)
  228. {
  229. parent->_bf = 1;
  230. subL->_bf = 0;
  231. subLR->_bf = 0;
  232. }
  233. else if (bf == 0)
  234. {
  235. parent->_bf = 0;
  236. subL->_bf = 0;
  237. subLR->_bf = 0;
  238. }
  239. else
  240. {
  241. //平衡因子出现其他值直接断言 - 防止出现其他问题
  242. assert(false);
  243. }
  244. }
  245. //右左双旋
  246. void RotateRL(Node* parent)
  247. {
  248. Node* subR = parent->_right;
  249. Node* subRL = subR->_left;
  250. int bf = subRL->_bf;
  251. RotateR(parent->_right); //右旋
  252. RotateL(parent); //左旋
  253. if (bf == 0)
  254. {
  255. parent->_bf = 0;
  256. subR->_bf = 0;
  257. subRL->_bf = 0;
  258. }
  259. else if (bf == -1)
  260. {
  261. parent->_bf = 0;
  262. subR->_bf = 1;
  263. subRL->_bf = 0;
  264. }
  265. else if (bf == 1)
  266. {
  267. parent->_bf = -1;
  268. subR->_bf = 0;
  269. subRL->_bf = 0;
  270. }
  271. else
  272. {
  273. assert(false);
  274. }
  275. }
  276. //计算高度
  277. int _Height(Node* root)
  278. {
  279. if (root == nullptr)
  280. return 0;
  281. int leftH = _Height(root->_left); //左子树高度
  282. int rightH = _Height(root->_right); //右子树高度
  283. return leftH > rightH ? leftH + 1 : rightH + 1; //返回的是整棵树的高度
  284. }
  285. //判断是否是AVL树 - 子函数
  286. bool _IsBalance(Node* root)
  287. {
  288. if (root == nullptr)
  289. return true;
  290. int left_h = _Height(root->_left);
  291. int right_h = _Height(root->_right);
  292. //检查平衡因子
  293. if (right_h - left_h != root->_bf)
  294. {
  295. cout << root->_kv.first << "节点平衡因子异常" << endl;
  296. return false;
  297. }
  298. //判断左右子树之间的差是否 < 2 abs:求绝对值
  299. return abs(left_h - right_h) < 2
  300. && _IsBalance(root->_left)
  301. && _IsBalance(root->_right);
  302. }
  303. protected:
  304. Node* _root = nullptr;
  305. };
  306. void Test_AVLTree1()
  307. {
  308. int arr1[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
  309. int arr2[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
  310. AVLTree<int, int> t1;
  311. for (auto e : arr1)
  312. {
  313. t1.Insert(make_pair(e, e));
  314. cout << e << "插入:" << t1.IsBalance() << endl; //插入进行检查
  315. }
  316. t1.InOrder();
  317. cout << t1.IsBalance() << endl;
  318. }

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

闽ICP备14008679号