• 【数据结构】二叉树---AVL树的实现


          

    目录

    一.  什么是AVL树

    二.  AVL树的结点结构定义

    三.  AVL树的动态平衡法

    1. 左单旋转 --- RL(RotateLeft) 型调整操作

    2. 右单旋转 --- RR(RotateRight) 型调整操作

    3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作

    4. 先右后左双旋转 --- RRL (RotateRightLeft) 型调整操作

    四.  AVL树的插入操作

    五.  AVL树的验证操作

    六.  完整源代码



    一.  什么是AVL树

    AVL树,又称平衡二叉树。   

           可以是一颗空树,或者是具有以下性质的二叉树:即它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度只差的绝对值不超过 1。 把二叉树上结点的平衡因子BF定义为该结点的左子树的高度和右子树的高度之差(即平衡二叉树上结点的平衡因子只可能是 -1、0 和 1

           只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

       说明:后面所用到的平衡因子的都是右子树的高的 - 左子树的高度

    二.  AVL树的结点结构定义

           影响二叉搜索树平衡的操作只能是插入和删除,这里已插入为例,同样一组数据元素插  入的顺序不同,二叉搜索树的形状就不同。也就需要一种动态平衡方法,当插入操作破坏了平衡,便可以用来调整。这种方式需要在原来二叉搜索树结点中增加一个量为平衡因子(BF)

    结点结构图

    在这里为了方便进行旋转操作对于AVL树的结点定义采用三叉链的结构

    1. //类模板结点的定义
    2. template <class T>
    3. struct AVLTreeNode
    4. {
    5. AVLTreeNode* _left;
    6. AVLTreeNode* _right;
    7. AVLTreeNode* _parent; //指向当前结点的父节点的指针
    8. AVLTreeNode _data;
    9. int _bf; //平衡因子
    10. //结点的构造函数
    11. AVLTreeNode(const T& data)
    12. :_left(nullptr)
    13. ,_right(nullptr)
    14. ,_parent(nullptr)
    15. ,_bf(0)
    16. ,_data(data)
    17. {}
    18. };

    三.  AVL树的动态平衡法

            如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构, 使之平衡化。根据节点插入位置的不同,AVL树的旋转分为以下四种

    1. 左单旋转 --- RL(RotateLeft) 型调整操作

          单向左旋平衡处理: 由于在subR这个结点的右子树上插入结点 ,subR的平衡因子由0变为1,p的平衡因子由1变为2,导致以p为根的子树失去平衡,则需进行一次向左的逆时针旋转操作

        链接操作:b链接到p的右;

                          p链接到subR的左;

                          subR成为当前树的根

    注意:1. 链接时subRL为空的情况

               2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)

    图示:

    1. //左单旋转
    2. void RotateL(Node* parent)
    3. {
    4. Node* subR = parent->_right;
    5. Node* subRL = subR->_left;
    6. //做左旋转(修改结点的指向)
    7. parent->_right = subRL;
    8. if (subRL) //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
    9. subRL->_parent = parent;
    10. subR->_left = parent;
    11. //修改各结点中父指针(_parent)的指向
    12. Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
    13. parent->_parent = subR; //修改parent中指向父节点的指针(_parent)
    14. if (parent == _root) //判断当前结点是否为根节点
    15. {
    16. _root = subR;
    17. subR->_parent = nullptr;
    18. }
    19. else
    20. {
    21. if (ppnode->_right == parent)
    22. {
    23. ppnode->_right = subR;
    24. }
    25. else
    26. {
    27. ppnode->_left = subR;
    28. }
    29. subR->_parent = ppnode;
    30. }
    31. //更新平衡因子
    32. parent->_bf = 0;
    33. subR->_bf = 0;
    34. }

    2. 右单旋转 --- RR(RotateRight) 型调整操作

      单向右旋平衡处理: 由于在subL的左子树上插入结点,subL的平衡因子由 0变成 -1 ,p的平衡因子由 1 变为 -2,导致以p为根的子树失去平衡,则需进行一次向右的顺时针旋转操作

       链接操作:b链接到p的左;

                         p链接到subL的右;

                         subL成为当前树的根

    注意:1. 链接时subLR为空的情况

               2. p可能是整棵树的子树 (p的上面可能还有结点) 或 整棵树的根 (p的上面无结点)

    图示:

    1. //右单旋转
    2. void RotateR(Node* parent)
    3. {
    4. Node* subL = parent->_left;
    5. Node* subLR = subL->_right;
    6. //做右旋转(修改结点的指向)
    7. parent->_left = subLR;
    8. if (subLR) //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
    9. subLR->_parent = parent;
    10. subL->_right = parent;
    11. //修改各结点中父指针(_parent)的指向
    12. Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
    13. parent->_parent = subL; //修改parent中指向父节点的指针(_parent)
    14. if (parent == _root) //判断当前结点是否为根节点
    15. {
    16. _root = subL;
    17. subL->_parent = nullptr;
    18. }
    19. else
    20. {
    21. if (ppnode->_left == parent)
    22. {
    23. ppnode->_left = subL;
    24. }
    25. else
    26. {
    27. ppnode->_right = subL;
    28. }
    29. subL->_parent = ppnode;
    30. }
    31. //更改平衡因子
    32. parent->_bf = 0;
    33. subL->_bf = 0;
    34. }

    3. 先左后右双旋转 --- RLR (RotateLeftRight) 型调整操作

      双向旋转(先左后右)平衡处理由于在subL的右子树上插入结点,subL的平衡因子由 0 变为 1,p的平衡因子由 -1 变为 -2,导致以p为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作

       链接操作:左单旋:b链接到subL的右;

                                        subL链接到subLR的左;

                                        subLR链接到p的左

                          右单旋:c链接到p的左;

                                         p链接到subLR的右;

                                         subLR成为当前子树的根

    图示:

    1. //先左后右双旋转
    2. void RotateLR(Node* parent)
    3. {
    4. Node* subL = parent->_left;
    5. Node* subLR = subL->_right;
    6. int bf = subLR->_bf; //先保存旋转前subLR结点的平衡因子
    7. RotateL(parent->_left); //左单旋转
    8. RotateR(parent); //右单旋转
    9. //更新旋转后的平衡因子
    10. if (bf == -1)
    11. {
    12. subLR->_bf = 0;
    13. subL->_bf = 0;
    14. parent->_bf = 1;
    15. }
    16. else if (bf == 1)
    17. {
    18. subLR->_bf = 0;
    19. subL->_bf = -1;
    20. parent->_bf = 0;
    21. }
    22. else if (bf == 0)
    23. {
    24. subLR->_bf = 0;
    25. subL->_bf = 0;
    26. parent->_bf = 0;
    27. }
    28. else
    29. {
    30. assert(false);
    31. }
    32. }

    4. 先右后左双旋转 --- RRL (RotateRightLeft) 型调整操作

      双向旋转(先右后左)平衡处理由于在subR的左子树上插入结点,subR的平衡因子由 0 变为 -1,p的平衡因子由 1 变为 2,导致以p为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作

       链接操作:右单旋:c链接到subR的左;

                                        subR链接到subRL的右;

                                        subRL链接到p的右

                         左单旋:b链接到p的右;

                                        p链接到subRL的左;

                                        subRL成为当前子树的根

         

    图示:

    1. //先右后左双旋转
    2. void RotateRL(Node* parent)
    3. {
    4. Node* subR = parent->_right;
    5. Node* subRL = subR->_left;
    6. int bf = subRL->_bf; //先保存旋转前subRL结点的平衡因子
    7. RotateR(subR); //右单旋转
    8. RotateL(parent); //左单旋转
    9. //更新旋转后的平衡因子
    10. if (bf == 1)
    11. {
    12. subRL->_bf = 0;
    13. subR->_bf = 0;
    14. parent->_bf = -1;
    15. }
    16. else if (bf == -1)
    17. {
    18. subRL->_bf = 0;
    19. subR->_bf = 1;
    20. parent->_bf = 0;
    21. }
    22. else if(bf==0)
    23. {
    24. subRL->_bf = 0;
    25. subR->_bf = 0;
    26. parent->_bf = 0;
    27. }
    28. else
    29. {
    30. assert(false);
    31. }
    32. }

    四.  AVL树的插入操作

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

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

           2. 调整节点的平衡因子      

            插入一个结点cur(当前要插入的结点)后,Parent的平衡因子一定需要调整,在插入之前,Parent 的平衡因子分为三种情况:-1,0, 1;

    分以下两种情况:  

          1. 如果cur插入到Parent的左侧,只需给 Parent 的平衡因子 减1 即可  

          2. 如果cur插入到Parent的右侧,只需给 Parent 的平衡因子 加1 即可  

    此时:Parent的平衡因子可能有三种情况:0,1或-1, 2或-2  

          1. 如果Parent的平衡因子为 0说明插入之前Parent的平衡因子为 1或-1插入后被调整成 0,此时满足 AVL树的性质,插入成功  

          2. 如果Parent的平衡因子为 1或-1,说明插入前Parent的平衡因子一定为 0,插入后被更新成 1或-1,此时,以Parent为根的树的高度增加,需要继续向上更新  

          3. 如果Parent的平衡因子为 2或-2,则Parent的平衡因子违反平衡树的性质,需要对其进行旋转处理

    1. bool Insert(const T& data)
    2. {
    3. //为空树
    4. if (_root == nullptr)
    5. {
    6. _root = new Node(data);
    7. return true;
    8. }
    9. //按照二叉搜索树的规则插入结点
    10. Node* parent = nullptr; //记录插入结点的父节点
    11. Node* cur = _root;
    12. while (cur)
    13. {
    14. if (cur->_data < data)
    15. {
    16. parent = cur;
    17. cur = cur->_right;
    18. }
    19. else if (cur->_data > data)
    20. {
    21. parent = cur;
    22. cur = cur->_left;
    23. }
    24. else
    25. {
    26. return false;
    27. }
    28. }
    29. //判断链接到父节点的那边
    30. cur = new Node(data);
    31. if (parent->_data > data)
    32. {
    33. parent->_left = cur;
    34. }
    35. else
    36. {
    37. parent->_right = cur;
    38. }
    39. //调整平衡因子 及 旋转调整
    40. cur->_parent = parent; //修改当前结点(cur)的父指针(_parent)的指向
    41. while (parent)
    42. {
    43. if (cur == parent->_left) //插入到父结点的左边,父节点的平衡因子--
    44. {
    45. parent->_bf--;
    46. }
    47. else //插入到父结点的左边,父节点的平衡因子--
    48. {
    49. parent->_bf++;
    50. }
    51. //
    52. if (parent->_bf == 0) //若(插入节点后)当前结点的平衡因子为零,则不会影响此节点的父及祖先结点;
    53. { //说明当前这颗子树插入节点后,其高度没有发生变化,也就不需要向上更新平衡因子
    54. break;
    55. }
    56. else if (parent->_bf == 1 || parent->_bf == -1) //若当前结点的平衡因子为1或-1,则会影响上面的祖先结点的平衡因子
    57. { //需要更新上面祖先的平衡因子
    58. cur = cur->_parent;
    59. parent = parent->_parent;
    60. }
    61. else if (parent->_bf == 2 || parent->_bf == -2)//若当前结点的平衡因子为2或-2,则需做旋转调整
    62. {
    63. //旋转
    64. if (parent->_bf == 2 && cur->_bf == 1)
    65. {
    66. RotateL(parent); //左单旋转
    67. }
    68. else if (parent->_bf == -2 && cur->_bf == -1)
    69. {
    70. RotateR(parent); //右单旋转
    71. }
    72. else if (parent->_bf == -2 && cur->_bf == 1)
    73. {
    74. RotateLR(parent); //先左单旋转,在右单旋转
    75. }
    76. else
    77. {
    78. RotateRL(parent); //先右单旋转,在左单旋转
    79. }
    80. break;
    81. }
    82. else //说明插入之前AVL数就有问题
    83. {
    84. assert(false);
    85. }
    86. }
    87. return true;
    88. }

    五.  AVL树的验证操作

    AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

         1. 验证其为二叉搜索树 如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

                验证方法:采用中序遍历即可

         2. 验证其为平衡树 每个节点子树高度差的绝对值不超过1

             (注意节点中如果没有平衡因子) 节点的平衡因子是否计算正确

                验证方法:1.验证每颗子树的左右高度差的绝对值是否超过 1(采用递归思想);

                                  2.验证结点的平衡因子是否正确

    1. //中序遍历
    2. void InOrder(Node* root)
    3. {
    4. if (root == nullptr)
    5. return;
    6. InOrder(root->_left);
    7. cout << root->_data << endl;
    8. InOrder(root->_right);
    9. }
    10. //求树的高度
    11. int _Height(Node* root)
    12. {
    13. if (root == nullptr)
    14. return 0;
    15. int leftheight = _Height(root->_left);
    16. int rightheight = _Height(root->_right);
    17. return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
    18. }
    19. // 验证AVL树的平衡
    20. bool _IsAVLTree1(Node* root) //前序遍历
    21. {
    22. if (root == nullptr)
    23. return true;
    24. int leftheight = _Height(root->_left); //左子树的高度
    25. int rightheight = _Height(root->_right); //右子树的高度
    26. //判断左右高度差是否超过1
    27. if (abs(rightheight - leftheight) >= 2)
    28. {
    29. cout << root->_data << "不平衡" << endl;
    30. return false;
    31. }
    32. //判断结点的平衡因子是否有异常
    33. if (rightheight - leftheight != root->_bf)
    34. {
    35. cout << root->_data << "平衡因子异常" << endl;
    36. return false;
    37. }
    38. return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
    39. }

    六.  完整源代码

    1. template <class T>
    2. struct AVLTreeNode
    3. {
    4. AVLTreeNode* _left;
    5. AVLTreeNode* _right;
    6. AVLTreeNode* _parent; //指向当前结点的父节点的
    7. int _bf; //平衡因子
    8. T _data;
    9. AVLTreeNode(const T& data)
    10. :_left(nullptr)
    11. ,_right(nullptr)
    12. ,_parent(nullptr)
    13. ,_bf(0)
    14. ,_data(data)
    15. {}
    16. };
    17. template<class T>
    18. class AVLTree
    19. {
    20. typedef AVLTreeNode Node;
    21. public:
    22. AVLTree()
    23. : _root(nullptr)
    24. {}
    25. bool Insert(const T& data)
    26. {
    27. if (_root == nullptr)
    28. {
    29. _root = new Node(data);
    30. return true;
    31. }
    32. Node* parent = nullptr;
    33. Node* cur = _root;
    34. while (cur)
    35. {
    36. if (cur->_data < data)
    37. {
    38. parent = cur;
    39. cur = cur->_right;
    40. }
    41. else if (cur->_data > data)
    42. {
    43. parent = cur;
    44. cur = cur->_left;
    45. }
    46. else
    47. {
    48. return false;
    49. }
    50. }
    51. cur = new Node(data);
    52. if (parent->_data > data)
    53. {
    54. parent->_left = cur;
    55. }
    56. else
    57. {
    58. parent->_right = cur;
    59. }
    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. //旋转
    84. if (parent->_bf == 2 && cur->_bf == 1)
    85. {
    86. RotateL(parent); //左单旋转
    87. }
    88. else if (parent->_bf == -2 && cur->_bf == -1)
    89. {
    90. RotateR(parent); //右单旋转
    91. }
    92. else if (parent->_bf == -2 && cur->_bf == 1)
    93. {
    94. RotateLR(parent); //先左单旋转,在右单旋转
    95. }
    96. else
    97. {
    98. RotateRL(parent); //先右单旋转,在左单旋转
    99. }
    100. break;
    101. }
    102. else //说明插入之前AVL数就有问题
    103. {
    104. assert(false);
    105. }
    106. }
    107. return true;
    108. }
    109. //左单旋转
    110. void RotateL(Node* parent)
    111. {
    112. Node* subR = parent->_right;
    113. Node* subRL = subR->_left;
    114. //做左旋转(修改结点的指向)
    115. parent->_right = subRL;
    116. if (subRL) //若subRL不为空,则修改subRL中指向父节点的指针(_parent)
    117. subRL->_parent = parent;
    118. subR->_left = parent;
    119. //修改各结点中父指针(_parent)的指向
    120. Node* ppnode = parent->_parent; //保存parent中父指针(_parent)的指向
    121. parent->_parent = subR; //修改parent中指向父节点的指针(_parent)
    122. if (parent == _root) //判断当前结点是否为根节点
    123. {
    124. _root = subR;
    125. subR->_parent = nullptr;
    126. }
    127. else
    128. {
    129. if (ppnode->_right == parent)
    130. {
    131. ppnode->_right = subR;
    132. }
    133. else
    134. {
    135. ppnode->_left = subR;
    136. }
    137. subR->_parent = ppnode;
    138. }
    139. //更新平衡因子
    140. parent->_bf = 0;
    141. subR->_bf = 0;
    142. }
    143. //右单旋转
    144. void RotateR(Node* parent)
    145. {
    146. Node* subL = parent->_left;
    147. Node* subLR = subL->_right;
    148. //做右旋转(修改结点的指向)
    149. parent->_left = subLR;
    150. if (subLR) //若subLR不为空,则修改subLR中指向父节点的指针(_parent)
    151. subLR->_parent = parent;
    152. subL->_right = parent;
    153. Node* ppnode = parent->_parent;
    154. parent->_parent = subL;
    155. if (parent == _root)
    156. {
    157. _root = subL;
    158. subL->_parent = nullptr;
    159. }
    160. else
    161. {
    162. if (ppnode->_left == parent)
    163. {
    164. ppnode->_left = subL;
    165. }
    166. else
    167. {
    168. ppnode->_right = subL;
    169. }
    170. subL->_parent = ppnode;
    171. }
    172. //更改平衡因子
    173. parent->_bf = 0;
    174. subL->_bf = 0;
    175. }
    176. //先左后右双旋转
    177. void RotateLR(Node* parent)
    178. {
    179. Node* subL = parent->_left;
    180. Node* subLR = subL->_right;
    181. int bf = subLR->_bf;
    182. RotateL(parent->_left);
    183. RotateR(parent);
    184. //更新旋转后的平衡因子
    185. if (bf == -1)
    186. {
    187. subLR->_bf = 0;
    188. subL->_bf = 0;
    189. parent->_bf = 1;
    190. }
    191. else if (bf == 1)
    192. {
    193. subLR->_bf = 0;
    194. subL->_bf = -1;
    195. parent->_bf = 0;
    196. }
    197. else if (bf == 0)
    198. {
    199. subLR->_bf = 0;
    200. subL->_bf = 0;
    201. parent->_bf = 0;
    202. }
    203. else
    204. {
    205. assert(false);
    206. }
    207. }
    208. //先右后左双旋转
    209. void RotateRL(Node* parent)
    210. {
    211. Node* subR = parent->_right;
    212. Node* subRL = subR->_left;
    213. int bf = subRL->_bf;
    214. RotateR(subR);
    215. RotateL(parent);
    216. //更新旋转后的平衡因子
    217. if (bf == 1)
    218. {
    219. subRL->_bf = 0;
    220. subR->_bf = 0;
    221. parent->_bf = -1;
    222. }
    223. else if (bf == -1)
    224. {
    225. subRL->_bf = 0;
    226. subR->_bf = 1;
    227. parent->_bf = 0;
    228. }
    229. else if(bf==0)
    230. {
    231. subRL->_bf = 0;
    232. subR->_bf = 0;
    233. parent->_bf = 0;
    234. }
    235. else
    236. {
    237. assert(false);
    238. }
    239. }
    240. //高度
    241. int Height()
    242. {
    243. return _Height(_root);
    244. }
    245. // AVL树的验证
    246. bool IsAVLTree()
    247. {
    248. return _IsAVLTree1(_root);
    249. }
    250. //中序遍历
    251. void InOrder()
    252. {
    253. _InOrder(_root);
    254. }
    255. private:
    256. int _Height(Node* root)
    257. {
    258. if (root == nullptr)
    259. return 0;
    260. int leftheight = _Height(root->_left);
    261. int rightheight = _Height(root->_right);
    262. return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
    263. }
    264. // AVL树的验证
    265. bool _IsAVLTree1(Node* root) //前序遍历
    266. {
    267. if (root == nullptr)
    268. return true;
    269. int leftheight = _Height(root->_left);
    270. int rightheight = _Height(root->_right);
    271. if (abs(rightheight - leftheight) >= 2)
    272. {
    273. cout << root->_data << "不平衡" << endl;
    274. return false;
    275. }
    276. if (rightheight - leftheight != root->_bf)
    277. {
    278. cout << root->_data << "平衡因子异常" << endl;
    279. return false;
    280. }
    281. return _IsAVLTree1(root->_left) && _IsAVLTree1(root->_right);
    282. }
    283. //中序遍历
    284. void _InOrder(Node* root)
    285. {
    286. if (root == nullptr)
    287. return;
    288. _InOrder(root->_left);
    289. cout << root->_data << endl;
    290. _InOrder(root->_right);
    291. }
    292. private:
    293. Node* _root;
    294. };


  • 相关阅读:
    解密Prompt系列28. LLM Agent之金融领域摸索:FinMem & FinAgent
    从编译器对指令集的要求看API设计原则
    凉鞋的 Unity 笔记 203. 变量的常用类型
    设置IDEA快捷生成方法头,类头注释
    Linux三剑客:awk的基本用法
    SparkSQL外部数据源
    LeetCode精选100题-【3数之和】-2
    冷知识:Mysql最大列限制和行限制
    [论文-ing]Weakly Supervised Universal Fracture Detectionin Pelvic X-rays
    怎么写出美观,可读性高的代码?
  • 原文地址:https://blog.csdn.net/HZ_ENG/article/details/136658987