当前位置:   article > 正文

C++|AVL树(模拟实现,测试)_avl树测试

avl树测试

目录

一、概念

二、AVL树的实现

2.1节点定义

2.2AVL树的基础架构

2.3AVL树的插入 

2.3.1按照二叉搜索树的方式插入新节点

2.3.2调整节点的平衡因子

2.3.2.1 新节点插入较高右子树的右侧:进行左单旋

 2.3.2.2新结点插入较高左子树的左侧:进行右单旋

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

2.3.2.4新结点插入较高右子树的左侧:先右单旋再左单旋

2.3.2.5调节平衡因子(完整版插入)

2.4AVL树的性能分析

三、AVL树的测试


一、概念

AVL树和红黑树都可以用来作为树形关联式容器的底层实现。但最终采用的是红黑树,虽然他们在时间复杂度上是一样的,但在某些场景下,红黑树更通用。他们依然是二叉搜索树,只不过实现二叉搜索树的方式不同,而被称做平衡二叉搜索树。

AVL树是由两名数学家(G.M.Adelson-Velskii和E.M.Landis)发明出来的一种解决二叉搜索树缺陷的方法,该树的名称也是这两位科学家的简称而得。

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

AVL树性质:

  • 他的左右子树都是AVL树
  • 左右子树高度之差的绝对值不超过1(-1/0/1),该高度之差也就是平衡因子

平衡因子= 右子树高度-左子树高度,也可以左减右,没有明确规定,那我采用的是右减左。

注意:是用高度之差计算出平衡因子,不是用平衡因子计算出平衡因子。其次平衡因子并不是必须的,他只是帮助我们更便捷的控制树

思考:既然了解了AVL树的规则,那为什么其高度差是不超过1,而不是0?

因为节点是一个一个插入的,有些情况无法做到左右子树高度相等,比如,2个节点的树,4个节点的树

更全面的平衡二叉搜索树如图:平衡因子记录高度差 

AVL树的高度是平衡的,高度可保持在log n,搜索时间复杂度为O(log N)。

二、AVL树的实现

2.1节点定义

节点中包括左指针,右指针,指向父节点的指针,平衡因子,要存储的数据值。 

  1. //节点
  2. template<class T>
  3. struct AVLTreeNode
  4. {
  5. AVLTreeNode(const T& data = T())
  6. :_left(nullptr)
  7. ,_right(nullptr)
  8. ,_parent(nullptr)
  9. ,_bf(0)
  10. ,_data(data)
  11. {
  12. }
  13. AVLTreeNode<T>* _left;
  14. AVLTreeNode<T>* _right;
  15. AVLTreeNode<T>* _parent;
  16. T _bf;//平衡因子
  17. T _data;//要存储的值
  18. };

2.2AVL树的基础架构

  1. template<class T>
  2. class AVLTree
  3. {
  4. typedef AVLTreeNode<T> Node;
  5. typedef Node* PNode;
  6. public:
  7. AVLTree()
  8. :_Root(nullptr)
  9. {}
  10. private:
  11. PNode _Root;
  12. };

2.3AVL树的插入 

 AVL树的就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:

1.按照二叉搜索树的方式插入新节点

2.调整节点的平衡因子

2.3.1按照二叉搜索树的方式插入新节点

  1. bool Insert(const T& data)
  2. {
  3. if (_Root == nullptr)
  4. {
  5. _Root = new Node(data);
  6. return true;
  7. }
  8. PNode cur = _Root;
  9. PNode parent = nullptr;
  10. //找插入点
  11. while (cur)
  12. {
  13. if (data < cur->_data)
  14. {
  15. parent = cur;
  16. cur = cur->_left;
  17. }
  18. else if (data > cur->_data)
  19. {
  20. parent = cur;
  21. cur = cur->_right;
  22. }
  23. else
  24. return false;
  25. }
  26. //插入
  27. PNode node = new Node(data);
  28. if (data < parent->_data)
  29. {
  30. parent->_left = node;
  31. }
  32. else
  33. {
  34. parent->_right = node;
  35. }
  36. node->_parent = parent;
  37. return ture;
  38. }

2.3.2调整节点的平衡因子

这里就有点漫长了,首先来了解一点,当我们插入节点时,会影响哪些节点的平衡因子?

会影响祖先的平衡因子,有时候是部分的,有时候是全部。至于为什么是这样,这就要来了解平衡因子的更新原则,如图:

 由上图可知根据节点的插入位置,来决定哪些祖先的平衡因子需要改变。

那么现在就要来重点讲一讲旋转问题了,当发生不平衡时,根据节点插入位置的不同,AVL树的旋转分为四种:

2.3.2.1 新节点插入较高右子树的右侧:进行左单旋

对于其插入,可以通过抽象图来做代表,通过具象图来进行更好的分析。 

 ​​​​​​​

 

根据图的分析,我们可将他转化为代码,其中要注意的是,当旋转完后,也要更新节点中指向父 节点的指针:

  1. //左单旋
  2. void RotateL(PNode parent)
  3. {
  4. PNode subR = parent->_right;
  5. PNode subRL = subR->_left;
  6. PNode pparent = parent->_parent;
  7. if (parent == _Root)//更新根节点
  8. {
  9. _Root = subR;
  10. }
  11. else
  12. {
  13. //更新parent的父节点指向
  14. if (parent == pparent->_left)
  15. {
  16. subR->_parent = pparent;
  17. pparent->_left = subR;
  18. }
  19. else
  20. {
  21. subR->_parent = pparent;
  22. pparent->_right = subR;
  23. }
  24. }
  25. //parent的右指针指向subRL,subRL的父节点指向parent
  26. parent->_right = subR->_left;
  27. if(subRL)//subR的左节点可能不存在
  28. subRL->_parent = parent;
  29. //subR的左指针指向parent,parent的父节点指向subR
  30. subR->_left = parent;
  31. parent->_parent = subR;
  32. //更新平衡因子
  33. parent->_bf = subR->_bf = 0;
  34. }
 2.3.2.2新结点插入较高左子树的左侧:进行右单旋

 

根据图的分析,我们可将他转化为代码:

  1. //右单旋
  2. void RotateR(PNode parent)
  3. {
  4. PNode subL = parent->_left;
  5. PNode subLR = subL->_right;
  6. PNode pparent = parent->_parent;
  7. if (_Root == parent)
  8. {
  9. _Root = subL;
  10. subL->_parent = nullptr;
  11. }
  12. else
  13. {
  14. //更新parent的父节点指向
  15. if (pparent->_left == parent)
  16. {
  17. pparent->_left = subL;
  18. }
  19. else
  20. {
  21. pparent->_right = subL;
  22. }
  23. subL->_parent = pparent;
  24. }
  25. //parent的左指针指向subLR,subLR的父节点指向parent
  26. parent->_left = subLR;
  27. if(subLR)//subR的右节点可能不存在
  28. subLR->_parent = parent;
  29. //subL的右指针指向parent,parent的父节点指向subL
  30. subL->_right = parent;
  31. parent->_parent = subL;
  32. //更新平衡因子
  33. parent->_bf = subL->_bf = 0;
  34. }
2.3.2.3新节点插入较高左子树的右侧:先左单旋再右单旋

有了上述的抽象图了解,这里就不在进行具象图分析,而是直接在抽象图上分析。 

当在右侧插入时,其也分为两种情况,为什么要区分这两种情况?

因为他们影响平衡因子的情况不同,所以要做区分。

 

 

 将上述情况转换为代码:

  1. //左右单旋
  2. void RotateLR(PNode parent)
  3. {
  4. PNode subL = parent->_left;
  5. PNode subLR = subL->_right;
  6. int bf = subLR->_bf;//记录下来,为了后面更新平衡因子
  7. RotateL(parent->_left);//左单旋
  8. RotateR(parent);//右单旋
  9. 因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
  10. //那么这里以原本subRL的平衡因子来做衡量
  11. if (bf == -1)
  12. {
  13. parent->_bf = 1;
  14. subL->_bf = 0;
  15. subLR->_bf = 0;
  16. }
  17. else if (bf == 1)
  18. {
  19. parent->_bf = 0;
  20. subL->_bf = -1;
  21. subLR->_bf = 0;
  22. }
  23. else if (bf == 0)//表示新插入节点
  24. {
  25. parent->_bf = subL->_bf = subLR->_bf = 0;
  26. }
  27. }
2.3.2.4新结点插入较高右子树的左侧:先右单旋再左单旋

 

 将上述情况转换为代码:

  1. //右左单旋
  2. void RotateRL(PNode parent)
  3. {
  4. PNode subR = parent->_right;
  5. PNode subRL = subR->_left;
  6. int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
  7. RotateR(parent->_right);//右单旋
  8. RotateL(parent);//左单旋
  9. //因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
  10. //那么这里以原本subRL的平衡因子来做衡量
  11. if (bf == 1)
  12. {
  13. parent->_bf = -1;
  14. subR->_bf = 0;
  15. subRL->_bf = 0;
  16. }
  17. else if (bf == -1)
  18. {
  19. parent->_bf = 0;
  20. subR->_bf = 1;
  21. subRL->_bf = 0;
  22. }
  23. else if (bf == 0)//表示新插入节点
  24. {
  25. parent->_bf = subR->_bf = subRL->_bf = 0;
  26. }
  27. }
2.3.2.5调节平衡因子(完整版插入)

当插入新结点时,更新祖先结点的平衡因子,并一直往上判断,根据祖先结点的平衡因子判断是否需要进行旋转。

  1. bool Insert(const T& data)
  2. {
  3. if (_Root == nullptr)
  4. {
  5. _Root = new Node(data);
  6. return true;
  7. }
  8. PNode cur = _Root;
  9. PNode parent = nullptr;
  10. //找插入点
  11. while (cur)
  12. {
  13. if (data < cur->_data)
  14. {
  15. parent = cur;
  16. cur = cur->_left;
  17. }
  18. else if (data > cur->_data)
  19. {
  20. parent = cur;
  21. cur = cur->_right;
  22. }
  23. else
  24. return false;
  25. }
  26. //插入
  27. PNode node = new Node(data);
  28. if (data < parent->_data)
  29. {
  30. parent->_left = node;
  31. }
  32. else
  33. {
  34. parent->_right = node;
  35. }
  36. node->_parent = parent;
  37. cur = node;
  38. while (cur)
  39. {
  40. if (parent == nullptr)
  41. break;
  42. //更新平衡因子
  43. if (cur == parent->_left)
  44. parent->_bf--;
  45. else
  46. parent->_bf++;
  47. if (parent->_bf == 0)//不会影响爷爷,直接插入结束
  48. {
  49. break;
  50. }
  51. else if(parent->_bf == 1 || parent->_bf == -1)//继续往上
  52. {
  53. cur = parent;
  54. parent = parent->_parent;
  55. }
  56. else if(parent->_bf == 2 || parent->_bf == -2)
  57. {
  58. if (cur == parent->_right)
  59. {
  60. if (cur->_bf == 1)
  61. {
  62. //左单旋
  63. RotateL(parent);
  64. }
  65. else if (cur->_bf == -1)
  66. {
  67. //右左单旋
  68. RotateRL(parent);
  69. }
  70. }
  71. else
  72. {
  73. if (cur->_bf == 1)
  74. {
  75. //左右单旋
  76. RotateLR(parent);
  77. }
  78. else if (cur->_bf == -1)
  79. {
  80. //右单旋
  81. RotateR(parent);
  82. }
  83. }
  84. break;
  85. }
  86. else
  87. {
  88. assert(false);//插入之前就有问题
  89. }
  90. }
  91. return true;
  92. }

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将结点删除,然后更新平衡因子,只不过,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

对于AVL树的删除,在这里就不在模拟实现了,因为有了插入的了解,就足够我们进行对AVL树的学习认识。

当然,如果想要进一步了解,可参考<<算法导论>>或<<数据结果-用面向南对象方法与C++描述>>殷人昆版。 

2.4AVL树的性能分析

 AVL树是一颗绝对平衡的二叉搜索树,弥补了普通版本的二叉搜索树的缺陷。其要求每个节点的左右子树高度差的绝对值不超过1,这样就可以保证查询时高效的时间复杂度,即log N。但是如果要

对AVL树做一些结构修改的操作,性能非常低下,

比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此 :如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会变),可以考虑AVL树,但一个结构经常修改,就不太适合。

三、AVL树的测试

 

  1. //AVLTree.h
  2. #include <iostream>
  3. #include <vector>
  4. #include <assert.h>
  5. using namespace std;
  6. namespace bit
  7. {
  8. //节点
  9. template<class T>
  10. struct AVLTreeNode
  11. {
  12. AVLTreeNode(const T& data = T())
  13. :_left(nullptr)
  14. ,_right(nullptr)
  15. ,_parent(nullptr)
  16. ,_bf(0)
  17. ,_data(data)
  18. {}
  19. AVLTreeNode<T>* _left;
  20. AVLTreeNode<T>* _right;
  21. AVLTreeNode<T>* _parent;
  22. T _bf;//平衡因子
  23. T _data;
  24. };
  25. template<class T>
  26. class AVLTree
  27. {
  28. typedef AVLTreeNode<T> Node;
  29. typedef Node* PNode;
  30. public:
  31. AVLTree()
  32. :_Root(nullptr)
  33. {}
  34. bool Insert(const T& data)
  35. {
  36. if (_Root == nullptr)
  37. {
  38. _Root = new Node(data);
  39. return true;
  40. }
  41. PNode cur = _Root;
  42. PNode parent = nullptr;
  43. //找插入点
  44. while (cur)
  45. {
  46. if (data < cur->_data)
  47. {
  48. parent = cur;
  49. cur = cur->_left;
  50. }
  51. else if (data > cur->_data)
  52. {
  53. parent = cur;
  54. cur = cur->_right;
  55. }
  56. else
  57. return false;
  58. }
  59. //插入
  60. PNode node = new Node(data);
  61. if (data < parent->_data)
  62. {
  63. parent->_left = node;
  64. }
  65. else
  66. {
  67. parent->_right = node;
  68. }
  69. node->_parent = parent;
  70. cur = node;
  71. while (cur)
  72. {
  73. if (parent == nullptr)
  74. break;
  75. //更新平衡因子
  76. if (cur == parent->_left)
  77. parent->_bf--;
  78. else
  79. parent->_bf++;
  80. if (parent->_bf == 0)//不会影响爷爷,直接插入结束
  81. {
  82. break;
  83. }
  84. else if(parent->_bf == 1 || parent->_bf == -1)//继续往上
  85. {
  86. cur = parent;
  87. parent = parent->_parent;
  88. }
  89. else if(parent->_bf == 2 || parent->_bf == -2)
  90. {
  91. if (cur == parent->_right)
  92. {
  93. if (cur->_bf == 1)
  94. {
  95. //左单旋
  96. RotateL(parent);
  97. }
  98. else if (cur->_bf == -1)
  99. {
  100. //右左单旋
  101. RotateRL(parent);
  102. }
  103. }
  104. else
  105. {
  106. if (cur->_bf == 1)
  107. {
  108. //左右单旋
  109. RotateLR(parent);
  110. }
  111. else if (cur->_bf == -1)
  112. {
  113. //右单旋
  114. RotateR(parent);
  115. }
  116. }
  117. break;
  118. }
  119. else
  120. {
  121. assert(false);//插入之前就有问题
  122. }
  123. }
  124. return true;
  125. }
  126. //左单旋
  127. void RotateL(PNode parent)
  128. {
  129. PNode subR = parent->_right;
  130. PNode subRL = subR->_left;
  131. PNode pparent = parent->_parent;
  132. if (parent == _Root)//更新根节点
  133. {
  134. _Root = subR;
  135. subR->_parent = nullptr;
  136. }
  137. else
  138. {
  139. //更新parent的父节点指向
  140. if (parent == pparent->_left)
  141. {
  142. pparent->_left = subR;
  143. }
  144. else
  145. {
  146. pparent->_right = subR;
  147. }
  148. subR->_parent = pparent;
  149. }
  150. //parent的右指针指向subRL,subRL的父节点指向parent
  151. parent->_right = subR->_left;
  152. if(subRL)//subR的左节点可能不存在
  153. subRL->_parent = parent;
  154. //subR的左指针指向parent,parent的父节点指向subR
  155. subR->_left = parent;
  156. parent->_parent = subR;
  157. //更新平衡因子
  158. parent->_bf = subR->_bf = 0;
  159. }
  160. //右单旋
  161. void RotateR(PNode parent)
  162. {
  163. PNode subL = parent->_left;
  164. PNode subLR = subL->_right;
  165. PNode pparent = parent->_parent;
  166. if (_Root == parent)
  167. {
  168. _Root = subL;
  169. subL->_parent = nullptr;
  170. }
  171. else
  172. {
  173. //更新parent的父节点指向
  174. if (pparent->_left == parent)
  175. {
  176. pparent->_left = subL;
  177. }
  178. else
  179. {
  180. pparent->_right = subL;
  181. }
  182. subL->_parent = pparent;
  183. }
  184. //parent的左指针指向subLR,subLR的父节点指向parent
  185. parent->_left = subLR;
  186. if(subLR)//subR的右节点可能不存在
  187. subLR->_parent = parent;
  188. //subL的右指针指向parent,parent的父节点指向subL
  189. subL->_right = parent;
  190. parent->_parent = subL;
  191. //更新平衡因子
  192. parent->_bf = subL->_bf = 0;
  193. }
  194. //左右单旋
  195. void RotateLR(PNode parent)
  196. {
  197. PNode subL = parent->_left;
  198. PNode subLR = subL->_right;
  199. int bf = subLR->_bf;//记录下来,为了后面更新平衡因子
  200. RotateL(parent->_left);//左单旋
  201. RotateR(parent);//右单旋
  202. 因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
  203. //那么这里以原本subRL的平衡因子来做衡量
  204. if (bf == -1)
  205. {
  206. parent->_bf = 1;
  207. subL->_bf = 0;
  208. subLR->_bf = 0;
  209. }
  210. else if (bf == 1)
  211. {
  212. parent->_bf = 0;
  213. subL->_bf = -1;
  214. subLR->_bf = 0;
  215. }
  216. else if (bf == 0)//表示新插入节点
  217. {
  218. parent->_bf = subL->_bf = subLR->_bf = 0;
  219. }
  220. }
  221. //右左单旋
  222. void RotateRL(PNode parent)
  223. {
  224. PNode subR = parent->_right;
  225. PNode subRL = subR->_left;
  226. int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
  227. RotateR(parent->_right);//右单旋
  228. RotateL(parent);//左单旋
  229. //因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
  230. //那么这里以原本subRL的平衡因子来做衡量
  231. if (bf == 1)
  232. {
  233. parent->_bf = -1;
  234. subR->_bf = 0;
  235. subRL->_bf = 0;
  236. }
  237. else if (bf == -1)
  238. {
  239. parent->_bf = 0;
  240. subR->_bf = 1;
  241. subRL->_bf = 0;
  242. }
  243. else if (bf == 0)//表示新插入节点
  244. {
  245. parent->_bf = subR->_bf = subRL->_bf = 0;
  246. }
  247. }
  248. void InOrder()
  249. {
  250. _InOrder(_Root);
  251. cout << endl;
  252. }
  253. int Height()
  254. {
  255. return _Height(_Root);
  256. }
  257. int _Height(PNode Root)
  258. {
  259. if (Root == nullptr)
  260. return 0;
  261. int _LeftHeight = _Height(Root->_left)+ 1;
  262. int _RightHeight = _Height(Root->_right) + 1;
  263. return max(_LeftHeight, _RightHeight);
  264. }
  265. bool _IsBalance()
  266. {
  267. return _IsBalanceTree(_Root);
  268. }
  269. bool _IsBalanceTree(PNode Root)
  270. {
  271. if (Root == nullptr)
  272. return true;
  273. int _LeftHeight = _Height(Root->_left);
  274. int _RightHeight = _Height(Root->_right);
  275. int sub =_RightHeight - _LeftHeight ;
  276. if (sub != Root->_bf)
  277. {
  278. cout << Root->_data << ":" << "平衡因子异常" << endl;
  279. return false;
  280. }
  281. if (sub == 1 || sub == -1 || sub == 0)
  282. {
  283. return _IsBalanceTree(Root->_left) && _IsBalanceTree(Root->_right);
  284. }
  285. else
  286. return false;
  287. }
  288. PNode Find(size_t data)
  289. {
  290. PNode cur = _Root;
  291. while (cur)
  292. {
  293. if (cur->_data < data)
  294. {
  295. cur = cur->_left;
  296. }
  297. else if (cur->_data > data)
  298. {
  299. cur = cur->_right;
  300. }
  301. else
  302. {
  303. return cur;
  304. }
  305. }
  306. return NULL;
  307. }
  308. size_t Size()
  309. {
  310. return _Size(_Root);
  311. }
  312. size_t _Size(PNode cur)
  313. {
  314. if (cur == nullptr)
  315. return 0;
  316. return _Size(cur->_left) + _Size(cur->_right) + 1;
  317. }
  318. private:
  319. void _InOrder(PNode Root)
  320. {
  321. if (Root == nullptr)
  322. return;
  323. _InOrder(Root->_left);
  324. cout << Root->_data<< endl;
  325. _InOrder(Root->_right);
  326. }
  327. PNode _Root;
  328. };
  329. void AVLTreeTest()
  330. {
  331. //int arr[] = { 16,3,7,11,9,26,18,14,15};
  332. int arr[] = { 4,2,6,1,3,5,15,7,16,14 };
  333. AVLTree<int> avl;
  334. for (auto e : arr)
  335. {
  336. avl.Insert(e);
  337. }
  338. avl.InOrder();
  339. cout <<avl._IsBalance();
  340. }
  341. void TestAVLTree2()
  342. {
  343. const int N = 9;
  344. vector<int> v;
  345. v.reserve(N);
  346. srand(time(0));
  347. for (size_t i = 0; i < N; i++)
  348. {
  349. v.push_back(rand() + i);
  350. //cout << v.back() << endl;
  351. }
  352. size_t begin2 = clock();
  353. AVLTree<int> t;
  354. for (auto e : v)
  355. {
  356. t.Insert(e);
  357. cout << "Insert:" << e << "->" << t._IsBalance() << endl;
  358. }
  359. size_t end2 = clock();
  360. cout << "Insert:" << end2 - begin2 << endl;
  361. cout << t._IsBalance() << endl;
  362. cout << "Height:" << t.Height() << endl;
  363. cout << "Size:" << t.Size() << endl;
  364. size_t begin1 = clock();
  365. // 确定在的值
  366. for (auto e : v)
  367. {
  368. t.Find(e);
  369. }
  370. // 随机值
  371. for (size_t i = 0; i < N; i++)
  372. {
  373. t.Find((rand() + i));
  374. }
  375. size_t end1 = clock();
  376. cout << "Find:" << end1 - begin1 << endl;
  377. }
  378. }
  1. //test.cpp
  2. #include "AVLTree.h"
  3. int main()
  4. {
  5. bit::AVLTreeTest();
  6. //bit::TestAVLTree2();
  7. return 0;
  8. }

输出结果:

  1. //test.cpp
  2. #include "AVLTree.h"
  3. int main()
  4. {
  5. //bit::AVLTreeTest();
  6. bit::TestAVLTree2();
  7. return 0;
  8. }

 输出结果:

end~ 

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

闽ICP备14008679号