当前位置:   article > 正文

AVL树C++实现——高度平衡二叉搜索树_avl树的查找操作c++

avl树的查找操作c++

目录

一、AVL树的概念

二、AVL 树节点的定义

三、AVL 树的插入

3.1 搜索树的插入

3.2 更新平衡因子的规则

3.4 左单旋调整

3.3 右单旋调整

3.5 先左单旋再右单旋

1. 右左插入

2. 右右插入

3. 右侧新增

3.5 先右单旋再左单旋

四、AVL 树测试

五、代码与测试用例


一、AVL树的概念

二叉搜索树可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于再顺序表中搜索元素,效率低下。

因此,两位俄罗斯数学家便发明了一种解决上述问题的方法:

当向二叉搜索树中插入新节点后,如果能保证每个结点左右子树高度之差的绝对值不超过1(需要对树的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

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

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

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

二、AVL 树节点的定义

二叉搜索树结点的定义与 AVL 树结点的定义在于:

  1. 三叉链模式,增加了父指针。
  2. 增加了平衡因子,方便我们调整平衡(可有可无,只是为了方便我们实现)

其数据的存储方式我们采用 K-value 模型。

三、AVL 树的插入

3.1 搜索树的插入

AVL 本质就是二叉搜索树,只不过 AVL 树是在二叉搜索树的基础上引入了平衡因子。即,在 AVL 树中的插入过程可以分为两步:

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

我们先来实现第一步,按二叉搜索树的方式插入新节点。

 这里就不过多介绍了,上一篇讲解过了二叉搜索树的插入。二叉搜索树的插入

  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. //开始插入
  29. cur = new Node(kv);
  30. if (parent->_kv.first < kv.first)
  31. {
  32. parent->_right = cur;
  33. }
  34. else
  35. {
  36. parent->_left = cur;
  37. }
  38. //因为增加了父域,所以还要处理cur的父域
  39. cur->_parent = parent;
  40. }

3.2 更新平衡因子的规则

以上我们完成了节点的插入,但是插入了节点后,树可能会出现以下情况:

 所以,为了防止以上情况出现,我们在插入后要调整平衡。

为了维持平衡,我们引入了平衡因子来控制树的平衡,所以我们每次插入后要更新平衡因子,如果出现失去平衡的情况,我们则要进行旋转来维持平衡。

更新平衡因子的规则:

  1. 新增在右边,则会让父平衡因子++,新增在左边,父平衡因子 -- ;
  2. 更新后,如果 parent->bf == 0,说明 parent 插入前的平衡因子是 1 or -1,插入填上了矮的一边,parent 的子树高度不变,不需要继续往上更新
  3. 更新后,如果 parent->bf 为 1 或 -1, 说明parent插入前的平衡因子是0,说明左右子树高度相等,插入后有一边高,parnet 高度变了,需要继续往上更新
  4. 更新后,如果 parent->bf == 2 或 -2 ,说明 parent 插入前的平衡因子是 1 or -1,已经到达平衡临界值,parent 子树进行旋转处理将树保持平衡
  5. 更新后,如果 parent->bf >2 或 <-2 ,则说明插入前树已经失去的平衡,要进行代码的检查。

现在,我们要对祖先路径更新平衡因子,直到更新到根节点结束。

注释我写的非常清楚,结合上图和代码一起理解。

 当规则4出现时,我们则要进行旋转来调整平衡,旋转共分为 4 种情况,左左右单旋、右右左单旋、左右左右双旋、右左右左双旋我们一一进行解决。

3.4 左单旋调整

新节点插入较高右子树的右侧 --- 右右:左单旋
观察下图,如果我们在 c 节点下插入新节点,导致 60的左右子树失去平衡,调整前后的情况如下:

 现在我们要发现左单旋的执行规律:

1.树的右子树失去平衡(parent->bf == 2),并且新插入的节点位于右子树的右侧(cur->bf == 1)。

2. 将 cur->left 交由 parent->right 抚养,再调整父子关系,使 cur->left 指向 parent ,即完成旋转。

3.平衡因子归零。观察上图,旋转后,60 的左右子树高度相同(a、b高度为h,c的高度为 h+1,注意:60左子树的高度也为h+1,因为存在 30 节点)

接下来我们编写代码

3.3 右单旋调整

新节点插入较高左子树的左侧-左左:右单旋。
观察下图,如果我们在 a 节点处插入新节点,导致 60的左右子树失去平衡,调整前后的情况如下:

 现在我们要发现右单旋的执行规律:

1.树的左子树失去平衡(parent->bf == -2),并且新插入的节点位于左子树的左侧(cur->bf == -1)。

2. 将 cur->right 交由 parent->left 抚养,再调整父子关系,使 cur->right 指向 parent ,即完成旋转。

右单旋就是方向相反的左单旋,理解了左单旋,右单旋实现起来非常简单, 代码如下。
  1. //情况2:左左右单旋
  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. Node* ppNode = parent->_parent;
  10. subL->_right = parent;
  11. parent->_parent = subL;
  12. if (ppNode)
  13. {
  14. if (ppNode->_left == parent)
  15. {
  16. ppNode->_left = subL;
  17. }
  18. else
  19. {
  20. ppNode->_right = subL;
  21. }
  22. subL->_parent = ppNode;
  23. }
  24. else
  25. {
  26. _root = subL;
  27. subL->_parent = nullptr;
  28. }
  29. subL->_bf = parent->_bf = 0;
  30. }

3.5 先左单旋再右单旋

上面我们解决了4种情况中的两种情况,对于平衡的调整还有左右左右双旋和右左右左双旋这两种情况。

其调整的条件是根的左子树的右侧插入了新的节点。

这种插入情况我们还要再细分为三种插入情况,其平衡因子会出现不同的情况:

  1. 在30的右子树 60 左侧进行插入。
  2. 在30的右子树 60 右侧进行插入。
  3. 30的右子树为空,插入的节点为60本身。

1. 右左插入

先分析第一种情况,如图:

此时 60 节点的左侧插入节点导致 90 节点下的左右子树高度失衡,所以我们接下来要对 90 节点下进行旋转处理。

旋转的步骤分为两步:

 先对30进行左单旋,然后再对90进行右单旋

1.先对 30 进行左单旋,这样,30节点的左右子树便恢复了平衡。

2.因为 60 左子树高度高于右子树,以 90节点视角来看,符合右单旋的条件(左高右低),所以90节点进行右单旋

然后对 90 进行右单旋

2. 右右插入

 以上情况是在 60 的左边进行插入,同样也有在 60 的右边进行插入,两种插入会导致 90、30的 平衡因子出现不同的情况。注意观察下图平衡因子的变化,与上图是不同的。

 然后对90进行右单旋

3. 右侧新增

 此时我们解决的都是 30 有左右子树,60有左右子树,还有一种可能,h为0,即60本身为新增。

然后进行旋转

 好的,三种情况我们都分析完了,发现其实无论哪种情况,其步骤都经历的左单旋和右单旋,只是平衡因子因情况的不同而不同。

而这三种情况的不同都是由 60 影响的,所以我们根据 60 平衡因子的不同分别进行调整即可。

 

3.5 先右单旋再左单旋

 同样的,右左右左双旋和左右左右双旋几乎一致,就不做过多讲解了,结合画图,仿照左右双旋,很容易写出代码。

 代码如下:

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

四、AVL 树测试

实现了插入我们来测试一下代码。

 好的,程序能正常运行,但程序能正常运行就说明我们的 AVL 就是成功的吗?我们的 AVL 树就是平衡的吗?

所以现在我们要使用检查树的高度的方式来检查树是否是平衡的。即,如果左右子树的高度差不超过1,则说明该树是平衡的,接下来我们来编写成员函数。

  1. int _Height(Node* root)
  2. {
  3. if (root == nullptr)
  4. return 0;
  5. return max(_Height(root->_left), _Height(root->_right)) + 1;
  6. }

有了测量高度的函数后我们来编写平衡判断函数。

  1. bool _IsBalance(Node* root)
  2. {
  3. if (root == nullptr)
  4. return true;
  5. int leftHeight = _Height(root->_left);
  6. int rightHeight = _Height(root->_right);
  7. int diffHeight = abs(leftHeight - rightHeight);
  8. // |diffHeight|小于2 则当前树平衡。
  9. // 还要去判断左右子树是否平衡
  10. return abs(diffHeight) < 2
  11. && _IsBalance(root->_left)
  12. && _IsBalance(root->_right);
  13. }

好的,接下来往树中插入一万个随机数据,然后来判断其是否平衡。

14层的平衡二叉树大约有16万个节点,所以高度计算正确,通过 IsBalance 函数也判定当前树为平衡。

最后我们来玩一下AVL树,往树中插入一亿个数据,然后找到77777处的数据。结果如下:

 一亿个数据,插入的效率是O(n*logN),想找到其中任意一个值,最多查找高度处。

五、代码与测试用例

  1. #include <iostream>
  2. #include <map>
  3. #include <utility>
  4. #include <assert.h>
  5. #pragma once
  6. using namespace std;
  7. //AVL树结点定义
  8. template<class K, class V>
  9. struct AVLTreeNode
  10. {
  11. AVLTreeNode <K, V>* _left;
  12. AVLTreeNode <K, V>* _right;
  13. AVLTreeNode <K, V>* _parent;
  14. pair<K, V> _kv; //key-value模型
  15. int _bf; //banlance factor 平衡因子
  16. AVLTreeNode(const pair<K, V>& kv)
  17. :_left(nullptr), _right(nullptr), _parent(nullptr)
  18. , _kv(kv)
  19. , _bf(0)
  20. {}
  21. };
  22. template<class K, class V>
  23. struct AVLTree
  24. {
  25. typedef AVLTreeNode<K, V> Node;
  26. public:
  27. bool Insert(const pair<K, V>& kv)
  28. {
  29. if (_root == nullptr)
  30. {
  31. _root = new Node(kv);
  32. return true;
  33. }
  34. Node* parent = nullptr;
  35. Node* cur = _root;
  36. while (cur)
  37. {
  38. if (cur->_kv.first < kv.first)
  39. {
  40. parent = cur;
  41. cur = cur->_right;
  42. }
  43. else if (cur->_kv.first > kv.first)
  44. {
  45. parent = cur;
  46. cur = cur->_left;
  47. }
  48. else
  49. {
  50. return false;
  51. }
  52. }
  53. cur = new Node(kv);
  54. if (parent->_kv.first < kv.first)
  55. {
  56. parent->_right = cur;
  57. }
  58. else
  59. {
  60. parent->_left = cur;
  61. }
  62. cur->_parent = parent;
  63. // 控制平衡
  64. // 1、更新平衡因子
  65. while (parent)
  66. {
  67. if (cur == parent->_right)
  68. {
  69. parent->_bf++;
  70. }
  71. else
  72. {
  73. parent->_bf--;
  74. }
  75. if (parent->_bf == 0)
  76. {
  77. break;
  78. }
  79. else if (abs(parent->_bf) == 1)
  80. {
  81. parent = parent->_parent;
  82. cur = cur->_parent;
  83. }
  84. else if (abs(parent->_bf) == 2)
  85. {
  86. // 说明parent所在子树已经不平衡了,需要旋转处理
  87. if (parent->_bf == 2 && cur->_bf == 1)
  88. {
  89. RotateL(parent);
  90. }
  91. else if ((parent->_bf == -2 && cur->_bf == -1))
  92. {
  93. RotateR(parent);
  94. }
  95. else if (parent->_bf == -2 && cur->_bf == 1)
  96. {
  97. RotateLR(parent);
  98. }
  99. else if (parent->_bf == 2 && cur->_bf == -1)
  100. {
  101. RotateRL(parent);
  102. }
  103. else
  104. {
  105. assert(false);
  106. }
  107. break;
  108. }
  109. else
  110. {
  111. assert(false);
  112. }
  113. }
  114. return true;
  115. }
  116. void Inorder()
  117. {
  118. _Inorder(_root);
  119. }
  120. int Height()
  121. {
  122. return _Height(_root);
  123. }
  124. bool IsBalance()
  125. {
  126. return _IsBalance(_root);
  127. }
  128. pair<K, V> Find(const K& key)
  129. {
  130. Node* cur = _root;
  131. while (cur)
  132. {
  133. if (cur->_kv.first > key)
  134. {
  135. cur = cur->_left;
  136. }
  137. else if (cur->_kv.first < key)
  138. {
  139. cur = cur->_right;
  140. }
  141. else
  142. {
  143. return cur->_kv;
  144. }
  145. }
  146. return pair<int, int>(0, 0);
  147. }
  148. private:
  149. bool _IsBalance(Node* root)
  150. {
  151. if (root == nullptr)
  152. return true;
  153. int leftHeight = _Height(root->_left);
  154. int rightHeight = _Height(root->_right);
  155. int diffHeight = abs(leftHeight - rightHeight);
  156. // |diffHeight|小于2 则当前树平衡。
  157. // 还要去判断左右子树是否平衡
  158. return abs(diffHeight) < 2
  159. && _IsBalance(root->_left)
  160. && _IsBalance(root->_right);
  161. }
  162. int _Height(Node* root)
  163. {
  164. if (root == nullptr)
  165. return 0;
  166. return max(_Height(root->_left), _Height(root->_right)) + 1;
  167. }
  168. void _Inorder(Node* root)
  169. {
  170. if (root)
  171. {
  172. _Inorder(root->_left);
  173. cout << root->_kv.first << ":" << root->_kv.second << endl;
  174. _Inorder(root->_right);
  175. }
  176. }
  177. void RotateL(Node* parent)
  178. {
  179. Node* subR = parent->_right;
  180. Node* subRL = subR->_left;
  181. parent->_right = subRL;
  182. if (subRL)
  183. subRL->_parent = parent;
  184. Node* ppNode = parent->_parent;
  185. subR->_left = parent;
  186. parent->_parent = subR;
  187. if (_root == parent)
  188. {
  189. _root = subR;
  190. subR->_parent = nullptr;
  191. }
  192. else
  193. {
  194. if (ppNode->_left == parent)
  195. {
  196. ppNode->_left = subR;
  197. }
  198. else
  199. {
  200. ppNode->_right = subR;
  201. }
  202. subR->_parent = ppNode;
  203. }
  204. subR->_bf = parent->_bf = 0;
  205. }
  206. void RotateR(Node* parent)
  207. {
  208. Node* subL = parent->_left;
  209. Node* subLR = subL->_right;
  210. parent->_left = subLR;
  211. if (subLR)
  212. {
  213. subLR->_parent = parent;
  214. }
  215. Node* ppNode = parent->_parent;
  216. subL->_right = parent;
  217. parent->_parent = subL;
  218. if (_root == parent)
  219. {
  220. _root = subL;
  221. subL->_parent = nullptr;
  222. }
  223. else
  224. {
  225. if (ppNode->_left == parent)
  226. {
  227. ppNode->_left = subL;
  228. }
  229. else
  230. {
  231. ppNode->_right = subL;
  232. }
  233. subL->_parent = ppNode;
  234. }
  235. subL->_bf = parent->_bf = 0;
  236. }
  237. void RotateLR(Node* parent)
  238. {
  239. Node* subL = parent->_left;
  240. Node* subLR = subL->_right;
  241. int bf = subLR->_bf;
  242. RotateL(parent->_left);
  243. RotateR(parent);
  244. subLR->_bf = 0;
  245. if (bf == 1)
  246. {
  247. parent->_bf = 0;
  248. subL->_bf = -1;
  249. }
  250. else if (bf == -1)
  251. {
  252. // 错的
  253. /*parent->_bf = 0;
  254. subL->_bf = 1;*/
  255. parent->_bf = 1;
  256. subL->_bf = 0;
  257. }
  258. else if (bf == 0)
  259. {
  260. parent->_bf = 0;
  261. subL->_bf = 0;
  262. }
  263. else
  264. {
  265. assert(false);
  266. }
  267. }
  268. void RotateRL(Node* parent)
  269. {
  270. Node* subR = parent->_right;
  271. Node* subRL = subR->_left;
  272. int bf = subRL->_bf;
  273. RotateR(parent->_right);
  274. RotateL(parent);
  275. subRL->_bf = 0;
  276. if (bf == 1)
  277. {
  278. subR->_bf = 0;
  279. parent->_bf = -1;
  280. }
  281. else if (bf == -1)
  282. {
  283. subR->_bf = 1;
  284. parent->_bf = 0;
  285. }
  286. else if (bf == 0)
  287. {
  288. parent->_bf = 0;
  289. subR->_bf = 0;
  290. }
  291. else
  292. {
  293. assert(false);
  294. }
  295. }
  296. private:
  297. Node* _root = nullptr;
  298. };
  299. void TestAVLTree1()
  300. {
  301. int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
  302. AVLTree<int, int> t1;
  303. for (auto e : a)
  304. {
  305. t1.Insert(make_pair(e, e));
  306. }
  307. t1.Inorder();
  308. cout << t1.Height() << endl;
  309. if (t1.IsBalance())
  310. cout << "平衡树" << endl;
  311. else
  312. cout << "非平衡树" << endl;
  313. }
  314. void TestAVLTree2()
  315. {
  316. //随机插入一万个数据
  317. size_t N = 10000;
  318. srand((unsigned int)time(NULL));
  319. AVLTree<int, int> t1;
  320. for (size_t i = 0; i < N; ++i)
  321. {
  322. int x = rand() % 100 + 1;
  323. t1.Insert(make_pair(i, x));
  324. bool IsBalance = t1.IsBalance();
  325. printf("%-4d:%-2d ", i, x);
  326. printf("Balance:%d\n", IsBalance);
  327. if (!IsBalance)
  328. {
  329. cout << i << endl;
  330. assert(false);
  331. }
  332. }
  333. //树的高度
  334. cout << "Tree Height:" << t1.Height() << endl;
  335. //判断平衡
  336. if (t1.IsBalance())
  337. cout << "IS AVL" << endl;
  338. else
  339. cout << "NOT AVL" << endl;
  340. }
  341. void TestAVLTree3()
  342. {
  343. size_t N = 100000000;
  344. srand((unsigned int)time(NULL));
  345. AVLTree<int, int> t1;
  346. for (size_t i = 0; i < N; ++i)
  347. {
  348. int x = rand() % 100 + 1;
  349. t1.Insert(make_pair(i, x));
  350. }
  351. cout << t1.Height() << endl;
  352. pair<int, int> result = t1.Find(777777);
  353. cout << result.first << ":" << result.second << endl;
  354. }
  355. int main()
  356. {
  357. //TestAVLTree1();
  358. //TestAVLTree2();
  359. TestAVLTree3();
  360. return 0;
  361. }

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

闽ICP备14008679号