AVL树是高度平衡的而二叉树。它的特点是:AVL树中任何节点的两个子树的高度最大差别为1。 
AVL树本质上还是一棵二叉搜索树,它的特点是:
1.本身首先是一棵二叉搜索树。
2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。
既然是树,那么就要有节点:

  1. template <class T>
  2. struct AVLTreeNode{
  3. T data;
  4. int height;
  5. AVLTreeNode* Left;
  6. AVLTreeNode* Right;
  7. AVLTreeNode(T v,AVLTreeNode* l,AVLTreeNode* r):data(v),height(0),Left(l),Right(r){}
  8. };
  9. /*
  10. 数据解释:
  11. data用来储存节点值
  12. height储存的是几点的高度
  13. Left是左儿子
  14. Right是右儿子
  15. 最后一项是构造函数
  16. */

View Code

接下来我们给出AVL树的定义

  1. template <class T>
  2. class AVLTree{
  3. private:
  4. //根节点
  5. AVLTreeNode<T>* Root;
  6. public:
  7. AVLTree():Root(NULL){}//构造函数
  8. void add(T data);//添加节点的外部接口
  9. int height();//查询高度的外部接口
  10. int max(int a, int b);//比较两个数据的大小
  11. private:
  12. AVLTreeNode<T>* add(AVLTreeNode<T>* &tree, T data);//添加节点的内部接口
  13. int height(AVLTreeNode<T>* tree);//查询高度的内部接口
  14. AVLTreeNode<T>* LL_Rotation(AVLTreeNode<T>* k2);//左左旋转的具体实现
  15. AVLTreeNode<T>* RR_Rotation(AVLTreeNode<T>* k1);//右右旋转的具体实现
  16. AVLTreeNode<T>* LR_Rotation(AVLTreeNode<T>* k3);//左右旋转的具体实现
  17. AVLTreeNode<T>* RL_Rotation(AVLTreeNode<T>* k1);//右左旋转的具体实现
  18. };

1.查询高度

  1. /*
  2. 高度
  3. 作用:获取树的高度
  4. */
  5. template <class T>
  6. int AVLTree<T>::height(AVLTreeNode<T>* tree)
  7. {
  8. if (tree != NULL)
  9. return tree->height;
  10. return 0;
  11. }
  12. template <class T>
  13. int AVLTree<T>::height() {
  14. return height(Root);
  15. }

2.比较大小

  1. /* 模板类改造比较两个值的大小*/
  2. template <class T>
  3. int AVLTree<T>::max(int a, int b) {
  4. return a>b ? a : b;
  5. }

3.旋转

如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右)和RL(右左)。下面给出它们的示意图:

上图中的4棵树都是”失去平衡的AVL树”,从左往右的情况依次是:LL、LR、RL、RR。除了上面的情况之外,还有其它的失去平衡的AVL树,如下图:

上面的两张图都是为了便于理解,而列举的关于”失去平衡的AVL树”的例子。总的来说,AVL树失去平衡时的情况一定是LL、LR、RL、RR这4种之一,它们都由各自的定义:

(1) LL:LeftLeft,也称为”左左”。插入或删除一个节点后,根节点的左子树的左子树还有非空子节点,导致”根的左子树的高度”比”根的右子树的高度”大2,导致AVL树失去了平衡。
     例如,在上面LL情况中,由于”根节点(8)的左子树(4)的左子树(2)还有非空子节点”,而”根节点(8)的右子树(12)没有子节点”;导致”根节点(8)的左子树(4)高度”比”根节点(8)的右子树(12)”高2。

 

(2) LR:LeftRight,也称为”左右”。插入或删除一个节点后,根节点的左子树的右子树还有非空子节点,导致”根的左子树的高度”比”根的右子树的高度”大2,导致AVL树失去了平衡。
     例如,在上面LR情况中,由于”根节点(8)的左子树(4)的左子树(6)还有非空子节点”,而”根节点(8)的右子树(12)没有子节点”;导致”根节点(8)的左子树(4)高度”比”根节点(8)的右子树(12)”高2。

 

(3) RL:RightLeft,称为”右左”。插入或删除一个节点后,根节点的右子树的左子树还有非空子节点,导致”根的右子树的高度”比”根的左子树的高度”大2,导致AVL树失去了平衡。
     例如,在上面RL情况中,由于”根节点(8)的右子树(12)的左子树(10)还有非空子节点”,而”根节点(8)的左子树(4)没有子节点”;导致”根节点(8)的右子树(12)高度”比”根节点(8)的左子树(4)”高2。

 

(4) RR:RightRight,称为”右右”。插入或删除一个节点后,根节点的右子树的右子树还有非空子节点,导致”根的右子树的高度”比”根的左子树的高度”大2,导致AVL树失去了平衡。
     例如,在上面RR情况中,由于”根节点(8)的右子树(12)的右子树(14)还有非空子节点”,而”根节点(8)的左子树(4)没有子节点”;导致”根节点(8)的右子树(12)高度”比”根节点(8)的左子树(4)”高2。

 

前面说过,如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。AVL失去平衡之后,可以通过旋转使其恢复平衡,下面分别介绍”LL(左左),LR(左右),RR(右右)和RL(右左)”这4种情况对应的旋转方法。

3.1LL旋转

  1. /*
  2. LL
  3. 在左左旋转中,一共涉及到三代节点,我们把爷爷节点命名为K2,K2的左儿子命名为K1。
  4. 问题出现的原因是K1的左儿子增加了一个节点导致平衡树失衡
  5. 解决思路:
  6. 让K1成为爷爷节点,K2成为K1的右儿子,并且将K1的右儿子接为K2的左儿子,然后返回爷爷节点K1取代原来K2的位置
  7. */
  8. template <class T>
  9. AVLTreeNode<T>* AVLTree<T>::LL_Rotation(AVLTreeNode<T>* k2){
  10. AVLTreeNode<T>* k1;
  11. k1 = k2->Left;
  12. k2->Left = k1->Right;
  13. k1->Right = k2;
  14. k2->height = max( height(k2->Left), height(k2->Right)) + 1;
  15. k1->height = max( height(k1->Left), k2->height) + 1;
  16. return k1;
  17. }

3.2RR旋转

  1. /*
  2. RR
  3. 在右右旋转中,一共涉及到三代节点,我们把爷爷节点命名为K1,K1的右儿子命名为K2。
  4. 问题出现的原因是K2的右儿子增加了一个节点导致平衡树失衡
  5. 解决思路:
  6. 让K2成为爷爷节点,K1成为K2的左儿子,并且将K2的左儿子接为K1的右儿子,然后返回爷爷节点K2取代原来K1的位置
  7. */
  8. template <class T>
  9. AVLTreeNode<T>* AVLTree<T>::RR_Rotation(AVLTreeNode<T>* k1){
  10. AVLTreeNode<T>* k2;
  11. k2 = k1->Right;
  12. k1->Right = k2->Left;
  13. k2->Left = k1;
  14. k1->height = max( height(k1->Left), height(k1->Right)) + 1;
  15. k2->height = max( height(k2->Right), k1->height) + 1;
  16. return k2;
  17. }

3.3LR旋转

 

  1. /*
  2. LR
  3. 在左右旋转中,一共涉及到四代节点,我们把做根本的节点成为K3(曾爷爷节点),K3的左儿子称为K1(爷爷节点),K1的右儿子称为K2
  4. 问题出现的原因时K2的右儿子增加了一个节点之后导致树的失衡
  5. 解决思路:
  6. 因为涉及到四代节点,所以需要两次旋转,
  7. 首先对K1,K2进行一次右右旋转 =》 K2成为爷爷节点(即K3的左儿子),k2原本的左儿子称为K1的右儿子,K1成为K2的左儿子
  8. 接下来对K2,K3进行一次左左旋转 =》K2称为曾爷爷节点,K2原本的右儿子成为K3的左儿子,K3成为K2的右儿子
  9. */
  10. template <class T>
  11. AVLTreeNode<T>* AVLTree<T>::LR_Rotation(AVLTreeNode<T>* k3){
  12. k3->Left = RR_Rotation(k3->Left);
  13. return LL_Rotation(k3);
  14. }

3.4RL旋转

  1. /*
  2. RL
  3. 在右左旋转中,一共涉及到四代节点,我们把做根本的节点成为K1(曾爷爷节点),K1的右儿子称为K3(爷爷节点),K3的左儿子称为K2
  4. 问题出现的原因时K2的左儿子增加了一个节点之后导致树的失衡
  5. 解决思路:
  6. 因为涉及到四代节点,所以需要两次旋转,
  7. 首先对K2,K3进行一次左左旋转 =》 K2成为爷爷节点(即K1的右儿子),k2原本的右儿子称为K3的左儿子,K3成为K2的右儿子
  8. 接下来对K1,K2进行一次右右旋转 =》K2称为曾爷爷节点,K2原本的左儿子成为K1的右儿子,K1成为K2的左儿子
  9. */
  10. template <class T>
  11. AVLTreeNode<T>* AVLTree<T>::RL_Rotation(AVLTreeNode<T>* k1){
  12. k1->Right = LL_Rotation(k1->Right);
  13. return RR_Rotation(k1);
  14. }

 4.插入节点

  1. template <class T>
  2. AVLTreeNode<T>* AVLTree<T>::add(AVLTreeNode<T>* &tree, T data){
  3. if (tree == NULL) {
  4. tree = new AVLTreeNode<T>(data, NULL, NULL);
  5. }
  6. else if (data < tree->data){
  7. //将新加入的节点插入左子树
  8. tree->Left = add(tree->Left, data);
  9. //检查加入新的结点之后树是否失去平衡
  10. if (height(tree->Left) - height(tree->Right) == 2)
  11. {
  12. if (data < tree->Left->data)
  13. tree = LL_Rotation(tree);//左左,新加入之后左儿子的左儿子深了
  14. else
  15. tree = LR_Rotation(tree);//左右,新加入之后左儿子的右儿子深了
  16. }
  17. }
  18. //将新加入的节点插入右子树
  19. else if (data > tree->data) {
  20. tree->Right = add(tree->Right, data);
  21. //检查加入新的结点之后树是否失去平衡
  22. if (height(tree->Right) - height(tree->Left) == 2)
  23. {
  24. if (data > tree->Right->data)
  25. tree = RR_Rotation(tree);//右右,新加入之后右儿子的右儿子深了
  26. else
  27. tree = RL_Rotation(tree);//右左,新加入之后右儿子的左儿子深了
  28. }
  29. }
  30. else //该节点已经在树中
  31. {
  32. cout << "该节点已经存在树中" << endl;
  33. }
  34. //更新更前当前节点的高度
  35. tree->height = max( height(tree->Left), height(tree->Right)) + 1;
  36. return tree;
  37. }
  38. template <class T>
  39. void AVLTree<T>::add(T data){
  40. add(Root, data);
  41. }

总的代码:

  1. #include <cstdio>
  2. #include <iostream>
  3.  
  4. using namespace std;
  5. template <class T>
  6. struct AVLTreeNode{
  7. T data;
  8. int height;
  9. AVLTreeNode* Left;
  10. AVLTreeNode* Right;
  11. AVLTreeNode(T v,AVLTreeNode* l,AVLTreeNode* r):data(v),height(0),Left(l),Right(r){}
  12. };
  13. /*
  14. AVL树的定义
  15. 为了保护类内数据,仿照网络实例把函数写成了内接口和外接口的形式。还有模板类。
  16. 感觉代码有点繁杂,写完之后调式的时候感觉不太顺手,以后写程序要注意内接口和外接口的模式
  17. */
  18. template <class T>
  19. class AVLTree{
  20. private:
  21. AVLTreeNode<T>* Root;
  22. public:
  23. AVLTree():Root(NULL){}
  24. void add(T data);
  25. int height();
  26. int max(int a, int b);
  27. private:
  28. AVLTreeNode<T>* add(AVLTreeNode<T>* &tree, T data);
  29. int height(AVLTreeNode<T>* tree);
  30. AVLTreeNode<T>* LL_Rotation(AVLTreeNode<T>* k2);
  31. AVLTreeNode<T>* RR_Rotation(AVLTreeNode<T>* k1);
  32. AVLTreeNode<T>* LR_Rotation(AVLTreeNode<T>* k3);
  33. AVLTreeNode<T>* RL_Rotation(AVLTreeNode<T>* k1);
  34. };
  35. /*
  36. 高度
  37. 作用:获取树的高度
  38. */
  39. template <class T>
  40. int AVLTree<T>::height(AVLTreeNode<T>* tree)
  41. {
  42. if (tree != NULL)
  43. return tree->height;
  44. return 0;
  45. }
  46. template <class T>
  47. int AVLTree<T>::height() {
  48. return height(Root);
  49. }
  50. /* 模板类改造比较两个值的大小*/
  51. template <class T>
  52. int AVLTree<T>::max(int a, int b) {
  53. return a>b ? a : b;
  54. }
  55. /*
  56. LL
  57. 在左左旋转中,一共涉及到三代节点,我们把爷爷节点命名为K2,K2的左儿子命名为K1。
  58. 问题出现的原因是K1的左儿子增加了一个节点导致平衡树失衡
  59. 解决思路:
  60. 让K1成为爷爷节点,K2成为K1的右儿子,并且将K1的右儿子接为K2的左儿子,然后返回爷爷节点K1取代原来K2的位置
  61. */
  62. template <class T>
  63. AVLTreeNode<T>* AVLTree<T>::LL_Rotation(AVLTreeNode<T>* k2){
  64. AVLTreeNode<T>* k1;
  65. k1 = k2->Left;
  66. k2->Left = k1->Right;
  67. k1->Right = k2;
  68. k2->height = max( height(k2->Left), height(k2->Right)) + 1;
  69. k1->height = max( height(k1->Left), k2->height) + 1;
  70. return k1;
  71. }
  72. /*
  73. RR
  74. 在右右旋转中,一共涉及到三代节点,我们把爷爷节点命名为K1,K1的右儿子命名为K2。
  75. 问题出现的原因是K2的右儿子增加了一个节点导致平衡树失衡
  76. 解决思路:
  77. 让K2成为爷爷节点,K1成为K2的左儿子,并且将K2的左儿子接为K1的右儿子,然后返回爷爷节点K2取代原来K1的位置
  78. */
  79. template <class T>
  80. AVLTreeNode<T>* AVLTree<T>::RR_Rotation(AVLTreeNode<T>* k1){
  81. AVLTreeNode<T>* k2;
  82. k2 = k1->Right;
  83. k1->Right = k2->Left;
  84. k2->Left = k1;
  85. k1->height = max( height(k1->Left), height(k1->Right)) + 1;
  86. k2->height = max( height(k2->Right), k1->height) + 1;
  87. return k2;
  88. }
  89. /*
  90. LR
  91. 在左右旋转中,一共涉及到四代节点,我们把做根本的节点成为K3(曾爷爷节点),K3的左儿子称为K1(爷爷节点),K1的右儿子称为K2
  92. 问题出现的原因时K2的右儿子增加了一个节点之后导致树的失衡
  93. 解决思路:
  94. 因为涉及到四代节点,所以需要两次旋转,
  95. 首先对K1,K2进行一次右右旋转 =》 K2成为爷爷节点(即K3的左儿子),k2原本的左儿子称为K1的右儿子,K1成为K2的左儿子
  96. 接下来对K2,K3进行一次左左旋转 =》K2称为曾爷爷节点,K2原本的右儿子成为K3的左儿子,K3成为K2的右儿子
  97. */
  98. template <class T>
  99. AVLTreeNode<T>* AVLTree<T>::LR_Rotation(AVLTreeNode<T>* k3){
  100. k3->Left = RR_Rotation(k3->Left);
  101. return LL_Rotation(k3);
  102. }
  103. /*
  104. RL
  105. 在右左旋转中,一共涉及到四代节点,我们把做根本的节点成为K1(曾爷爷节点),K1的右儿子称为K3(爷爷节点),K3的左儿子称为K2
  106. 问题出现的原因时K2的左儿子增加了一个节点之后导致树的失衡
  107. 解决思路:
  108. 因为涉及到四代节点,所以需要两次旋转,
  109. 首先对K2,K3进行一次左左旋转 =》 K2成为爷爷节点(即K1的右儿子),k2原本的右儿子称为K3的左儿子,K3成为K2的右儿子
  110. 接下来对K1,K2进行一次右右旋转 =》K2称为曾爷爷节点,K2原本的左儿子成为K1的右儿子,K1成为K2的左儿子
  111. */
  112. template <class T>
  113. AVLTreeNode<T>* AVLTree<T>::RL_Rotation(AVLTreeNode<T>* k1){
  114. k1->Right = LL_Rotation(k1->Right);
  115. return RR_Rotation(k1);
  116. }
  117. template <class T>
  118. AVLTreeNode<T>* AVLTree<T>::add(AVLTreeNode<T>* &tree, T data){
  119. if (tree == NULL) {
  120. tree = new AVLTreeNode<T>(data, NULL, NULL);
  121. }
  122. else if (data < tree->data){
  123. //将新加入的节点插入左子树
  124. tree->Left = add(tree->Left, data);
  125. //检查加入新的结点之后树是否失去平衡
  126. if (height(tree->Left) - height(tree->Right) == 2)
  127. {
  128. if (data < tree->Left->data)
  129. tree = LL_Rotation(tree);//左左,新加入之后左儿子的左儿子深了
  130. else
  131. tree = LR_Rotation(tree);//左右,新加入之后左儿子的右儿子深了
  132. }
  133. }
  134. //将新加入的节点插入右子树
  135. else if (data > tree->data) {
  136. tree->Right = add(tree->Right, data);
  137. //检查加入新的结点之后树是否失去平衡
  138. if (height(tree->Right) - height(tree->Left) == 2)
  139. {
  140. if (data > tree->Right->data)
  141. tree = RR_Rotation(tree);//右右,新加入之后右儿子的右儿子深了
  142. else
  143. tree = RL_Rotation(tree);//右左,新加入之后右儿子的左儿子深了
  144. }
  145. }
  146. else //该节点已经在树中
  147. {
  148. cout << "该节点已经存在树中" << endl;
  149. }
  150. //更新更前当前节点的高度
  151. tree->height = max( height(tree->Left), height(tree->Right)) + 1;
  152. return tree;
  153. }
  154. template <class T>
  155. void AVLTree<T>::add(T data){
  156. add(Root, data);
  157. }
  158. int main(){
  159. int num;
  160. AVLTree<int>* tree=new AVLTree<int>();
  161. cin>>num;
  162. for(int i=0;i<num;i++){
  163. int x;
  164. cin>>x;
  165. tree->add(x);
  166. }
  167. cout<<"高度为:"<<tree->height()<<endl;
  168. return 0;
  169. }
  170. /*
  171. 实例输入:
  172. 16
  173. 3 2 1 4 5 6 7 16 15 14 13 12 11 10 8 9
  174. 实例输出:
  175. 5
  176. */

View Code

 

版权声明:本文为yuemo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/yuemo/p/9783189.html