• 数据结构:AVL树


    目录

    1、AVL树的概念

    2、二叉搜索树的功能与实现

    1、AVL树节点定义

    2、AVL树的插入

    3、AVL树的旋转操作

    1、左旋

    2、右旋

    3、左右旋

     4、右左旋

     3、AVL树完整代码实现


    1、AVL树的概念

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

    一棵 AVL 树或者是空树,或者是具有以下性质的二叉搜索树:
    它的左右子树都是 AVL
    左右子树高度之差 ( 简称平衡因子 ) 的绝对值不超过 1(-1/0/1)
    平衡因子:右子树高度减去左子树高度
    如果一棵二叉搜索树是高度平衡的,它就是 AVL 树。如果它有 n 个结点,其高度可保持在
    O(log2 n) ,搜索时间复杂度 O(log2 n)
    以下是一个简单AVL树的示例:节点周围的是平衡因子。

    2、二叉搜索树的功能与实现

    1、AVL树节点定义

    1. template<class K,class V>
    2. struct AVLTreeNode
    3. {
    4. AVLTreeNode* _left;
    5. AVLTreeNode* _right;
    6. AVLTreeNode* _parent;
    7. int _bf; //平衡因子
    8. pair _kv;
    9. AVLTreeNode(const pair& kv)
    10. :_left(nullptr)
    11. , _right(nullptr)
    12. , _parent(nullptr)
    13. , _bf(0)
    14. ,_kv(kv)
    15. {
    16. }
    17. };
    每个节点的平衡因子设置为0,每个节点包含左右指针,以及父节点指针,每个节点的数据用pair来实现,第一个元素为Key来比较。

    2、AVL树的插入

    AVL树是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看作二叉搜索树

    插入过程与二叉搜索树一致,只不过要注意更新节点的平衡因子。

    因为平衡因子是右子树高度减去左子树高度,所以如果在左子树添加节点,bf(平衡因子)--,在右子树添加,bf++。

    parent节点的平衡因子更新有三种情况:

    1、parent的平衡因子为0,说明满足AVL性质,插入成功。

    2、parent的平衡因子为1或-1,说明该树的高度增加,需要向上更新。

    3、parent的平衡因子为-2或2,说明该节点违反了平衡树性质,需要对其进行旋转处理。

    旋转处理下面会介绍,我们先来看插入的代码实现:

    1. bool Insert(const pair& 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. while (cur)
    11. {
    12. if (cur->_kv.first < kv.first)
    13. {
    14. parent = cur;
    15. cur = cur->_right;
    16. }
    17. else if (cur->_kv.first > kv.first)
    18. {
    19. parent = cur;
    20. cur = cur->_left;
    21. }
    22. else
    23. {
    24. return false;
    25. }
    26. }
    27. cur = new Node(kv);
    28. if (parent->_kv.first < kv.first)
    29. {
    30. parent->_right = cur;
    31. }
    32. else
    33. {
    34. parent->_left = cur;
    35. }
    36. cur->_parent = parent;
    37. while (parent)
    38. {
    39. if (cur == parent->_left)
    40. {
    41. parent->_bf--;
    42. }
    43. else
    44. {
    45. parent->_bf++;
    46. }
    47. if (parent->_bf == 0)
    48. {
    49. break;
    50. }
    51. else if (parent->_bf == 1 || parent->_bf == -1)
    52. {
    53. cur = cur->_parent;
    54. parent = parent->_parent;
    55. }
    56. else if (parent->_bf == 2 || parent->_bf == -2)
    57. {
    58. if (parent->_bf == 2 && cur->_bf == 1)
    59. {
    60. RotateL(parent);
    61. }
    62. else if (parent->_bf == -2 && cur->_bf == -1)
    63. {
    64. RotateR(parent);
    65. }
    66. else if (parent->_bf == -2 && cur->_bf == 1)
    67. {
    68. RotateLR(parent);
    69. }
    70. else
    71. {
    72. RotateRL(parent);
    73. }
    74. break;
    75. }
    76. else
    77. {
    78. assert(false);
    79. }
    80. }
    81. return true;
    82. }

    寻找插入位置与搜索二叉树一致,然后更新平衡因子 ,对于不同的违反AVL树性质需要不同的旋转操作。

    3、AVL树的旋转操作

    我们先来看左旋对应的情况:

    1、左旋

    a,b,c具有相同的高度,旋转后注意更新parent和cur的平衡因子为0;
    1. void RotateL(Node* parent)
    2. {
    3. Node* sub = parent->_right;
    4. Node* subl = sub->_left;
    5. parent->_right = subl;
    6. if (subl)
    7. {
    8. subl->_parent = parent;
    9. }
    10. sub->_left = parent;
    11. Node* ppnode = parent->_parent;
    12. parent->_parent = sub;
    13. if (parent == _root)
    14. {
    15. _root = sub;
    16. sub->_parent = nullptr;
    17. }
    18. else
    19. {
    20. if (parent == ppnode->_left)
    21. {
    22. ppnode->_left = sub;
    23. }
    24. else
    25. {
    26. ppnode->_right = sub;
    27. }
    28. sub->_parent = ppnode;
    29. }
    30. parent->_bf = 0;
    31. sub->_bf = 0;
    32. }

    2、右旋

    原理与左旋相似,不过是向右旋转而已 (a,b,c具有相同的高度)

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

    3、左右旋

    先以subl为根左旋,再以parent为根进行右旋。(a,b,c具有相同的高度)

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

    根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。

     4、右左旋

    原理与左右旋相似,只是换了个方向。(a,b,c具有相同的高度)

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

    根据sublr的平衡因子的不同(也就是插入到了B还是C)来判断如何更新平衡因子。 

     3、AVL树完整代码实现

    内部包含查找以及判断是否是AVL树的函数,以及中序遍历。

    1. #pragma once
    2. namespace AVLTree_test
    3. {
    4. template<class K,class V>
    5. struct AVLTreeNode
    6. {
    7. AVLTreeNode* _left;
    8. AVLTreeNode* _right;
    9. AVLTreeNode* _parent;
    10. int _bf; //平衡因子
    11. pair _kv;
    12. AVLTreeNode(const pair& kv)
    13. :_left(nullptr)
    14. , _right(nullptr)
    15. , _parent(nullptr)
    16. , _bf(0)
    17. ,_kv(kv)
    18. {
    19. }
    20. };
    21. template<class K,class V>
    22. class AVLTree
    23. {
    24. typedef AVLTreeNode Node;
    25. public:
    26. bool Insert(const pair& kv)
    27. {
    28. if (_root == nullptr)
    29. {
    30. _root = new Node(kv);
    31. return true;
    32. }
    33. Node* parent = nullptr;
    34. Node* cur = _root;
    35. while (cur)
    36. {
    37. if (cur->_kv.first < kv.first)
    38. {
    39. parent = cur;
    40. cur = cur->_right;
    41. }
    42. else if (cur->_kv.first > kv.first)
    43. {
    44. parent = cur;
    45. cur = cur->_left;
    46. }
    47. else
    48. {
    49. return false;
    50. }
    51. }
    52. cur = new Node(kv);
    53. if (parent->_kv.first < kv.first)
    54. {
    55. parent->_right = cur;
    56. }
    57. else
    58. {
    59. parent->_left = cur;
    60. }
    61. cur->_parent = parent;
    62. while (parent)
    63. {
    64. if (cur == parent->_left)
    65. {
    66. parent->_bf--;
    67. }
    68. else
    69. {
    70. parent->_bf++;
    71. }
    72. if (parent->_bf == 0)
    73. {
    74. break;
    75. }
    76. else if (parent->_bf == 1 || parent->_bf == -1)
    77. {
    78. cur = cur->_parent;
    79. parent = parent->_parent;
    80. }
    81. else if (parent->_bf == 2 || parent->_bf == -2)
    82. {
    83. if (parent->_bf == 2 && cur->_bf == 1)
    84. {
    85. RotateL(parent);
    86. }
    87. else if (parent->_bf == -2 && cur->_bf == -1)
    88. {
    89. RotateR(parent);
    90. }
    91. else if (parent->_bf == -2 && cur->_bf == 1)
    92. {
    93. RotateLR(parent);
    94. }
    95. else
    96. {
    97. RotateRL(parent);
    98. }
    99. break;
    100. }
    101. else
    102. {
    103. assert(false);
    104. }
    105. }
    106. return true;
    107. }
    108. void RotateL(Node* parent)
    109. {
    110. Node* sub = parent->_right;
    111. Node* subl = sub->_left;
    112. parent->_right = subl;
    113. if (subl)
    114. {
    115. subl->_parent = parent;
    116. }
    117. sub->_left = parent;
    118. Node* ppnode = parent->_parent;
    119. parent->_parent = sub;
    120. if (parent == _root)
    121. {
    122. _root = sub;
    123. sub->_parent = nullptr;
    124. }
    125. else
    126. {
    127. if (parent == ppnode->_left)
    128. {
    129. ppnode->_left = sub;
    130. }
    131. else
    132. {
    133. ppnode->_right = sub;
    134. }
    135. sub->_parent = ppnode;
    136. }
    137. parent->_bf = 0;
    138. sub->_bf = 0;
    139. }
    140. void RotateR(Node* parent)
    141. {
    142. Node* sub = parent->_left;
    143. Node* subr = sub->_right;
    144. parent->_left = subr;
    145. if (subr)
    146. {
    147. subr->_parent = parent;
    148. }
    149. sub->_right = parent;
    150. Node* ppnode = parent->_parent;
    151. parent->_parent = sub;
    152. if (parent == _root)
    153. {
    154. _root = sub;
    155. sub->_parent = nullptr;
    156. }
    157. else
    158. {
    159. if (parent == ppnode->_left)
    160. {
    161. ppnode->_left = sub;
    162. }
    163. else
    164. {
    165. ppnode->_right = sub;
    166. }
    167. sub->_parent = parent;
    168. }
    169. parent->_bf = 0;
    170. sub->_bf = 0;
    171. }
    172. void RotateLR(Node* parent)
    173. {
    174. Node* subl = parent->_left;
    175. Node* sublr = subl->_right;
    176. int bf = sublr->_bf;
    177. RotateL(subl);
    178. RotateR(parent);
    179. if (bf == -1)
    180. {
    181. sublr->_bf = 0;
    182. parent->_bf = 1;
    183. subl->_bf = 0;
    184. }
    185. else if (bf == 1)
    186. {
    187. sublr->_bf = 0;
    188. parent->_bf = 0;
    189. subl->_bf = -1;
    190. }
    191. else if (bf == 0)
    192. {
    193. subl->_bf = 0;
    194. parent->_bf = 0;
    195. sublr->_bf = 0;
    196. }
    197. else
    198. {
    199. assert(false);
    200. }
    201. }
    202. void RotateRL(Node* parent)
    203. {
    204. Node* subr = parent->_right;
    205. Node* subrl = subr->_left;
    206. int bf = subrl->_bf;
    207. RotateR(subr);
    208. RotateL(parent);
    209. if (bf == -1)
    210. {
    211. subrl->_bf = 0;
    212. parent->_bf = 0;
    213. subr->_bf = 1;
    214. }
    215. else if (bf == 1)
    216. {
    217. subrl->_bf = 0;
    218. parent->_bf = -1;
    219. subr->_bf = 0;
    220. }
    221. else if(bf==0)
    222. {
    223. subrl->_bf = 0;
    224. parent->_bf = 0;
    225. subr->_bf = 0;
    226. }
    227. else
    228. {
    229. assert(false);
    230. }
    231. }
    232. void _InOrder(Node* root)
    233. {
    234. if (root == nullptr)
    235. return;
    236. _InOrder(root->_left);
    237. cout << root->_kv.first << " " << root->_bf << endl;
    238. _InOrder(root->_right);
    239. }
    240. void InOrder()
    241. {
    242. _InOrder(_root);
    243. }
    244. int Height(Node* root)
    245. {
    246. if (root == nullptr)
    247. {
    248. return 0;
    249. }
    250. int leftHeight = Height(root->_left);
    251. int rightHeight = Height(root->_right);
    252. return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
    253. }
    254. bool _IsBalance(Node* root)
    255. {
    256. if (root == nullptr)
    257. return true;
    258. int leftHeight = Height(root->_left);
    259. int rightHeight = Height(root->_right);
    260. if (abs(rightHeight - leftHeight) >= 2)
    261. {
    262. cout << root->_kv.first << "不平衡" << endl;
    263. return false;
    264. }
    265. if (rightHeight - leftHeight != root->_bf)
    266. {
    267. cout << root->_kv.first << "平衡因子异常" << endl;
    268. return false;
    269. }
    270. return _IsBalance(root->_left) && _IsBalance(root->_right);
    271. }
    272. bool IsBalance()
    273. {
    274. return _IsBalance(_root);
    275. }
    276. Node* Find(const K& key)
    277. {
    278. Node* cur = _root;
    279. while (cur)
    280. {
    281. if (cur->_kv.first < key)
    282. {
    283. cur = cur->_right;
    284. }
    285. else if (cur->_kv.first > key)
    286. {
    287. cur = cur->_left;
    288. }
    289. else
    290. {
    291. return cur;
    292. }
    293. }
    294. return NULL;
    295. }
    296. private:
    297. Node* _root = nullptr;
    298. };
    299. void TestAVLTree1()
    300. {
    301. int a[] = { 4, 2, 6, 1,0 ,67,56,33,212,90};
    302. AVLTree<int, int> t;
    303. for (auto e : a)
    304. {
    305. if (e == 14)
    306. {
    307. int x = 0;
    308. }
    309. t.Insert(make_pair(e,e));
    310. }
    311. t.InOrder();
    312. cout << t.IsBalance();
    313. }
    314. }

  • 相关阅读:
    王道机试C++第 4 章 字符串:字符串内容详解及三个小程序 Day29
    自媒体人必看的9个网站,每一个都很实用,值得收藏
    797. 所有可能的路径
    爬虫ip如何加入到代码里实现自动化数据抓取
    怎样评测对比报表工具的性能?
    机器学习:可解释学习
    Leetcode 2862. Maximum Element-Sum of a Complete Subset of Indices
    css笔记
    linux下golang环境安装教程(学习笔记)
    Grafana Loki查询加速:如何在不添加资源的前提下提升查询速度
  • 原文地址:https://blog.csdn.net/decade777555/article/details/136538683