• C++进阶篇4---番外-AVL树


    一、AVL树的概念

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

    【注意】平衡因子是用右子树的高度减去左子树的高度得到的

     

    二、AVL树结点的定义

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

     三、AVL树的插入

    AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。
    AVL树的插入过程可以分为两步:
    1. 按照二叉搜索树的方式插入新节点
    2. 调整节点的平衡因子
    1. template<class K,class V>
    2. class AVLTree {
    3. typedef AVLTreeNode Node;
    4. public:
    5. bool insert(const pair& kv)
    6. {
    7. if (_root == nullptr)
    8. {
    9. _root = new Node(kv);
    10. return true;
    11. }
    12. Node* cur = _root;
    13. Node* parent = nullptr;
    14. while (cur)
    15. {
    16. if(cur->_kv.first>kv.first)
    17. {
    18. parent = cur;
    19. cur = cur->_left;
    20. }
    21. else if (cur->_kv.first < kv.first)
    22. {
    23. parent = cur;
    24. cur = cur->_right;
    25. }
    26. else
    27. {
    28. return false;
    29. }
    30. }
    31. cur = new Node(kv);
    32. cur->_parent = parent;
    33. if (parent->_kv.first > kv.first )
    34. {
    35. parent->_left = cur;
    36. }
    37. else
    38. {
    39. parent->_right = cur;
    40. }
    41. //看树是否还保持平衡
    42. while (parent)
    43. {
    44. //先调整平衡因子---因为插入的结点是叶子节点,所以父结点的平衡因子必然发生变化
    45. //在根据平衡因子的计算公式height_r - height_l,判断平衡因子的变化
    46. if (parent->_left == cur)
    47. {
    48. parent->_bf--;
    49. }
    50. else
    51. {
    52. parent->_bf++;
    53. }
    54. //看是否需要调整以及如何调整
    55. //...
    56. }
    57. }
    58. private:
    59. Node* _root = nullptr;
    60. };

    上面代码的插入逻辑和二叉搜索树很相似,这里不多讲了(忘记的或者不了解的可以去看二叉搜索树),主要看如何判断树是否平衡以及如何调整使得树保持平衡

    这里主要分三种情况:

    1、父节点的平衡因子变成0,则树保持平衡,不需要变化

    解释:父节点的平衡因子变成0,说明之前未正负1,只有如下两种情况

    2、父节点的平衡因子变成正负1,则该子树的高度发生变化,但该子树依旧平衡,要看它的父节点所在的子树是否还能保持平衡

    3、父结点的平衡因子变成正负2,则该子树的不能保持平衡,需要进行旋转调整

    1. template<class K,class V>
    2. class AVLTree {
    3. typedef AVLTreeNode Node;
    4. public:
    5. bool insert(const pair& kv)
    6. {
    7. if (_root == nullptr)
    8. {
    9. _root = new Node(kv);
    10. return true;
    11. }
    12. Node* cur = _root;
    13. Node* parent = nullptr;
    14. while (cur)
    15. {
    16. if(cur->_kv.first>kv.first)
    17. {
    18. parent = cur;
    19. cur = cur->_left;
    20. }
    21. else if (cur->_kv.first < kv.first)
    22. {
    23. parent = cur;
    24. cur = cur->_right;
    25. }
    26. else
    27. {
    28. return false;
    29. }
    30. }
    31. cur = new Node(kv);
    32. cur->_parent = parent;
    33. if (parent->_kv.first > kv.first )
    34. {
    35. parent->_left = cur;
    36. }
    37. else
    38. {
    39. parent->_right = cur;
    40. }
    41. //看树是否还保持平衡
    42. while (parent)
    43. {
    44. //先调整平衡因子
    45. if (parent->_left == cur)
    46. {
    47. parent->_bf--;
    48. }
    49. else
    50. {
    51. parent->_bf++;
    52. }
    53. if (parent->_bf == 0)
    54. {
    55. break;
    56. }
    57. else if (parent->_bf == 1 || parent->_bf == -1)
    58. {
    59. cur = parent;
    60. parent = parent->_parent;
    61. }
    62. else if (parent->_bf == 2 || parent->_bf == -2)
    63. {
    64. //分4种情况:左单旋,右单旋,先左旋在右旋,先右旋在左旋
    65. //...
    66. //旋转完成后子树就平衡了=> 整个树都平衡了,直接退出循环
    67. break;
    68. }
    69. else
    70. {
    71. //如果进入这里,说明前面的代码出错
    72. assert(0);
    73. }
    74. }
    75. }
    76. private:
    77. Node* _root = nullptr;
    78. };

     四、旋转调整

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

    代码如下

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

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

    注意事项同上。

    代码如下 

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

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

    代码如下

    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. parent->_bf = subR->_bf = subRL->_bf = 0;
    11. }
    12. else if (bf == 1)
    13. {
    14. parent->_bf = -1;
    15. subR->_bf = subRL->_bf = 0;
    16. }
    17. else
    18. {
    19. subR->_bf = 1;
    20. parent->_bf = subRL->_bf = 0;
    21. }
    22. }

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

    这个留给读者思考 

    附:

    1. //完整版代码
    2. template<class K,class V>
    3. struct AVLTreeNode {
    4. AVLTreeNode* _left;
    5. AVLTreeNode* _right;
    6. AVLTreeNode* _parent;
    7. pair _kv;
    8. int _bf;
    9. AVLTreeNode(const pair& kv)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _parent(nullptr)
    13. ,_kv(kv)
    14. ,_bf(0)
    15. {}
    16. };
    17. template<class K,class V>
    18. class AVLTree {
    19. typedef AVLTreeNode Node;
    20. public:
    21. bool insert(const pair& kv)
    22. {
    23. if (_root == nullptr)
    24. {
    25. _root = new Node(kv);
    26. return true;
    27. }
    28. Node* cur = _root;
    29. Node* parent = nullptr;
    30. while (cur)
    31. {
    32. if (cur->_kv.first > kv.first)
    33. {
    34. parent = cur;
    35. cur = cur->_left;
    36. }
    37. else if (cur->_kv.first < kv.first)
    38. {
    39. parent = cur;
    40. cur = cur->_right;
    41. }
    42. else
    43. {
    44. return false;
    45. }
    46. }
    47. cur = new Node(kv);
    48. cur->_parent = parent;
    49. if (parent->_kv.first > kv.first)
    50. {
    51. parent->_left = cur;
    52. }
    53. else
    54. {
    55. parent->_right = cur;
    56. }
    57. while (parent)
    58. {
    59. if (parent->_left == cur)
    60. {
    61. parent->_bf--;
    62. }
    63. else
    64. {
    65. parent->_bf++;
    66. }
    67. if (parent->_bf == 0)//为0,说明之前_bf=-1/1,即子树的高度没有发生变化
    68. {
    69. break;
    70. }
    71. else if (parent->_bf == 1 || parent->_bf == -1)//为正负1,说明之前_bf=0,即子树的高度发生变化,并且会影响到上层祖宗结点
    72. {
    73. cur = parent;
    74. parent = parent->_parent;
    75. }
    76. else if (parent->_bf == 2 || parent->_bf == -2)//为正负2,树明显不平衡,需要旋转调整
    77. {
    78. if (parent->_bf == 2 && cur->_bf == 1)//如果该子树的严格右边高,则左单旋
    79. {
    80. _RotateL(parent);
    81. }
    82. else if (parent->_bf == -2 && cur->_bf == -1)//如果该子树的严格左边高,则右单旋
    83. {
    84. _RotateR(parent);
    85. }
    86. else if (parent->_bf == -2 && cur->_bf == 1)//左右旋
    87. {
    88. _RotateLR(parent);
    89. }
    90. else if (parent->_bf == 2 && cur->_bf == -1)//右旋左旋
    91. {
    92. _RotateRL(parent);
    93. }
    94. break;//旋转之后整个树就平衡了,直接跳出循环
    95. }
    96. else
    97. {
    98. //这种情况不可能发生,如果发生就说明程序出错
    99. assert(false);
    100. }
    101. }
    102. return true;
    103. }
    104. void _RotateLR(Node* parent)
    105. {
    106. Node* subL = parent->_left;
    107. Node* subLR = subL->_right;
    108. int bf = subLR->_bf;
    109. _RotateL(parent->_left);
    110. _RotateR(parent);
    111. if (bf == 1)
    112. {
    113. subL->_bf = -1;
    114. subLR->_bf = parent->_bf = 0;
    115. }
    116. else if(bf == -1)
    117. {
    118. parent->_bf = 1;
    119. subL->_bf = subLR->_bf = 0;
    120. }
    121. else//bf==0,插入的结点就是subLR
    122. {
    123. parent->_bf = subL->_bf = subLR->_bf = 0;
    124. }
    125. }
    126. void _RotateRL(Node* parent)
    127. {
    128. Node* subR = parent->_right;
    129. Node* subRL = subR->_left;
    130. int bf = subRL->_bf;
    131. _RotateR(parent->_right);
    132. _RotateL(parent);
    133. if (bf == 1)
    134. {
    135. parent->_bf = -1;
    136. subR->_bf = subRL->_bf = 0;
    137. }
    138. else if(bf == -1)
    139. {
    140. subR->_bf = 1;
    141. parent->_bf = subRL->_bf = 0;
    142. }
    143. else //bf==0,插入的结点就是subLR
    144. {
    145. parent->_bf = subR->_bf = subRL->_bf = 0;
    146. }
    147. }
    148. //右单旋
    149. void _RotateR(Node*parent)
    150. {
    151. Node* subL = parent->_left;//找到要作为新根的结点
    152. Node* pParent = parent->_parent;//找到该子树的父亲结点
    153. Node* subLR = subL->_right;
    154. subL->_right = parent;
    155. parent->_parent = subL;
    156. parent->_left = subLR;
    157. if (subLR)
    158. subLR->_parent = parent;
    159. if (_root == parent)//如果是根
    160. {
    161. _root = subL;
    162. subL->_parent = nullptr;
    163. }
    164. else
    165. {
    166. subL->_parent = pParent;
    167. if (pParent->_left == parent)
    168. {
    169. pParent->_left = subL;
    170. }
    171. else
    172. {
    173. pParent->_right = subL;
    174. }
    175. }
    176. parent->_bf = subL->_bf = 0;
    177. }
    178. //左单旋
    179. void _RotateL(Node* parent)
    180. {
    181. Node* subR = parent->_right;//找到要作为新根的结点
    182. Node* pParent = parent->_parent;//找到该子树的父亲结点
    183. Node* subRL = subR->_left;//找到要被"过继"的孩子结点
    184. subR->_left = parent;
    185. if (subRL)//如有"过继"结点
    186. subRL->_parent = parent;
    187. parent->_parent = subR;
    188. parent->_right = subRL;
    189. if (_root == parent)//如果是根
    190. {
    191. _root = subR;
    192. subR->_parent = nullptr;
    193. }
    194. else
    195. {
    196. subR->_parent = pParent;
    197. if (pParent->_left == parent)
    198. {
    199. pParent->_left = subR;
    200. }
    201. else
    202. {
    203. pParent->_right = subR;
    204. }
    205. }
    206. parent->_bf = subR->_bf = 0;
    207. }
    208. bool Isbalance()
    209. {
    210. return _Isbalance(_root);
    211. //return _Isbalance(_root) >= 0;
    212. }
    213. bool _Isbalance(Node*root)
    214. {
    215. if (root == nullptr)
    216. return true;
    217. int left = _Height(root->_left);
    218. int right = _Height(root->_right);
    219. if (abs(right - left) > 1)
    220. {
    221. cout << root->_kv.first << ":" << root->_kv.second << endl;
    222. return false;
    223. }
    224. if (right - left != root->_bf)
    225. {
    226. cout << root->_kv.first << ":"<< "平衡因子出错" << endl;
    227. return false;
    228. }
    229. return _Isbalance(root->_left) && _Isbalance(root->_right);
    230. }
    231. size_t size()
    232. {
    233. return _size(_root);
    234. }
    235. size_t Height()
    236. {
    237. return _Height(_root);
    238. }
    239. private:
    240. size_t _Height(Node*root)
    241. {
    242. if (root == nullptr)
    243. return 0;
    244. return max(_Height(root->_left),_Height(root->_right)) + 1;
    245. }
    246. size_t _size(Node* root)
    247. {
    248. if (root == nullptr)
    249. return 0;
    250. return 1 + _size(root->_left) + _size(root->_right);
    251. }
    252. //如果单纯判断是否平衡可以这么写,-1表示不平衡,>=0表示平衡
    253. //int _Isbalance(Node* root)
    254. //{
    255. // if (root == nullptr)
    256. // return 0;
    257. // int left = _Isbalance(root->_left);
    258. // if (left < 0) return -1;
    259. // int right = _Isbalance(root->_right);
    260. // if (right < 0) return -1;
    261. // if (abs(right - left) > 1 || right - left != root->_bf)
    262. // {
    263. // return -1;
    264. // }
    265. // return max(left, right) + 1;
    266. //}
    267. private:
    268. Node* _root = nullptr;
    269. };

  • 相关阅读:
    物联网网关助力生产数据可视化,提升智能管理水平
    若依3.6.0使用Mybatis-plus分页失效以及完美替换Pagehelper【已解决】
    Python:实现merge sort归并排序算法(附完整源码)
    猿创征文|深度学习基于ResNet18网络完成图像分类
    相机不小心格式化了怎么恢复?如何快速找回珍贵照片
    记一次有趣的逻辑SRC挖掘
    Android Qcom Display学习(十)
    neo4j load csv 配置和使用
    短视频矩阵系统源码技术独立部署搭建
    【计算机视觉 | 目标检测】目标检测常用数据集及其介绍(十二)
  • 原文地址:https://blog.csdn.net/V_zjs/article/details/134266459