• AVL 平衡二叉搜索树


    二叉搜索树(BST)虽能缩短查找效率,但如果数据有序或接近有序,BST将退化为单支树,此时查找元素就相当于在顺序表中搜索元素,效率低下。

    那么此时如果能保证每个结点的左右子树高度差的绝对值不超过1,就可以降低树的高度,从而减少平均搜索长度。所以AVL带着这个使命诞生了。

    一、AVL 

    在二叉树中,如果每个结点的子树的高度差距为0、1或-1,则称这棵树是平衡二叉树,即AVL。

    二、平衡二叉树

    执行插入或删除操作后如果导致了AVL的不平衡,那么我们需要执行旋转操作来重新平衡这棵树。旋转操作主要有LL、RR、LR、RL四种类型。

    1、LL

    左子树中的左孩子插入新结点后导致不平衡,此时需要右旋操作:

    旋转后平衡二叉树将B看作新root, 将E结点与A.left相连,再将A与B.right相连,最后得到新的AVL。

    1. private void balanceLL(AVLTreeNode<E> A, TreeNode<E> parentOfA){
    2. TreeNode<E> B = A.left;
    3. if(A == root){
    4. root = B;
    5. }else{
    6. if(parentOfA.left == A){
    7. parentOfA.left = B;
    8. }else{
    9. parentOfA.right = B;
    10. }
    11. }
    12. A.left = B.right;
    13. B.right = A;
    14. updateHeight((AVLTreeNode<E>) A);

    2、RR

    和LL相反,向右子树中的右孩子插入新结点后导致的不平衡,此时需要左旋操作: 

    1. private void balanceRR(TreeNode<E> A, TreeNode<E> parentOfA){
    2. TreeNode<E> B =A.right;
    3. if(A == root){
    4. root = B;
    5. }else{
    6. if(parentOfA.left == A){
    7. parentOfA.left = B;
    8. }else{
    9. parentOfA.right = B;
    10. }
    11. }
    12. A.right = B.left;
    13. B.left = A;
    14. updateHeight((AVLTreeNode<E>)A);
    15. updateHeight((AVLTreeNode<E>)B);
    16. }

    3、LR 

    左儿子左旋,根结点右旋

    1. private void balanceLR(TreeNode<E> A, TreeNode<E> parentOfA){
    2. TreeNode<E> B = A.left;
    3. TreeNode C = B.right;
    4. if(A == root){
    5. root = C;
    6. }else{
    7. if(parentOfA.left == A){
    8. parentOfA.left = C;
    9. }else {
    10. parentOfA.right = C;
    11. }
    12. }
    13. A.left = C.right;
    14. B.right = C.left;
    15. C.left = B;
    16. C.right = A;
    17. updateHeight((AVLTreeNode<E>)A);
    18. updateHeight((AVLTreeNode<E>)B);
    19. updateHeight((AVLTreeNode<E>)C);
    20. }

    4、RL

    右儿子右旋,根结点左旋:

    1. private void balanceRL(TreeNode<E> A, TreeNode<E> parentOfA){
    2. TreeNode<E> B = A.right;
    3. TreeNode<E> C = B.left;
    4. if(A == root){
    5. root = C;
    6. }else{
    7. if(parentOfA.left == A){
    8. parentOfA.left = C;
    9. }else{
    10. parentOfA.right = C;
    11. }
    12. }
    13. A.right = C.left;
    14. B.left = C.right;
    15. C.left = A;
    16. C.right = B;
    17. updateHeight((AVLTreeNode<E>)A);
    18. updateHeight((AVLTreeNode<E>)B);
    19. updateHeight((AVLTreeNode<E>)C);
    20. }

     三、Java实现AVLTree类

    AVLTree类继承BST类。

    1. public class AVLTree<E extends Comparable<E>> extends BST<E>{
    2. public AVLTree() {
    3. }
    4. public AVLTree(E[] objects) {
    5. super(objects);
    6. }
    7. // 插入方法和BST中的方法一样,只不过每次添加后需要检查平衡
    8. @Override
    9. public boolean insert(E e){
    10. boolean success = super.insert(e);
    11. if(!success){
    12. return false;
    13. }else{
    14. balancePath(e); // 检查是否需要平衡操作
    15. }
    16. return true;
    17. }
    18. private void updateHeight(AVLTreeNode<E> node){
    19. if(node.left == null && node.right == null){
    20. node.height = 0;
    21. }else if(node.left == null){
    22. node.height = 1 + ((AVLTreeNode<E>)(node.right)).height;
    23. }else if(node.right == null){
    24. node.height = 1 + ((AVLTreeNode<E>)(node.left)).height;
    25. }else
    26. node.height = 1 + Math.max(((AVLTreeNode<E>)(node.right)).height,((AVLTreeNode<E>)(node.left)).height);
    27. }
    28. // 平衡一条路径上的结点
    29. private void balancePath(E e){
    30. java.util.ArrayList<TreeNode<E>> path = path(e); // 获取包含e的结点到根结点的路径
    31. for(int i = path.size() - 1; i >= 0; i--){
    32. AVLTreeNode<E> A = (AVLTreeNode<E>)(path.get(i));
    33. updateHeight(A); // 更新高度
    34. AVLTreeNode<E> parentOfA = (A == root) ? null : (AVLTreeNode<E>)(path.get(i - 1));
    35. switch(balanceFactor(A)){ // 检查平衡因子,检查是否需要旋转
    36. case -2 :
    37. if(balanceFactor((AVLTreeNode<E>)A.left) <= 0){
    38. balanceLL(A,parentOfA);
    39. }else{
    40. balanceLR(A,parentOfA);
    41. }
    42. break;
    43. case +2 :
    44. if(balanceFactor((AVLTreeNode<E>)A.right) >= 0){
    45. balanceRR(A, parentOfA);
    46. }else{
    47. balanceRL(A, parentOfA);
    48. }
    49. }
    50. }
    51. }
    52. private int balanceFactor(AVLTreeNode<E> node){
    53. if(node.left == null){
    54. return -node.height;
    55. }else if(node.left == null){
    56. return +node.height;
    57. }else
    58. return ((AVLTreeNode<E>)node.right).height - ((AVLTreeNode<E>)node.left).height;
    59. }
    60. private void balanceLL(AVLTreeNode<E> A, TreeNode<E> parentOfA){
    61. TreeNode<E> B = A.left;
    62. if(A == root){
    63. root = B;
    64. }else{
    65. if(parentOfA.left == A){
    66. parentOfA.left = B;
    67. }else{
    68. parentOfA.right = B;
    69. }
    70. }
    71. A.left = B.right;
    72. B.right = A;
    73. updateHeight((AVLTreeNode<E>) A);
    74. updateHeight((AVLTreeNode<E>) B);
    75. }
    76. private void balanceLR(TreeNode<E> A, TreeNode<E> parentOfA){
    77. TreeNode<E> B = A.left;
    78. TreeNode C = B.right;
    79. if(A == root){
    80. root = C;
    81. }else{
    82. if(parentOfA.left == A){
    83. parentOfA.left = C;
    84. }else {
    85. parentOfA.right = C;
    86. }
    87. }
    88. A.left = C.right;
    89. B.right = C.left;
    90. C.left = B;
    91. C.right = A;
    92. updateHeight((AVLTreeNode<E>)A);
    93. updateHeight((AVLTreeNode<E>)B);
    94. updateHeight((AVLTreeNode<E>)C);
    95. }
    96. private void balanceRR(TreeNode<E> A, TreeNode<E> parentOfA){
    97. TreeNode<E> B =A.right;
    98. if(A == root){
    99. root = B;
    100. }else{
    101. if(parentOfA.left == A){
    102. parentOfA.left = B;
    103. }else{
    104. parentOfA.right = B;
    105. }
    106. }
    107. A.right = B.left;
    108. B.left = A;
    109. updateHeight((AVLTreeNode<E>)A);
    110. updateHeight((AVLTreeNode<E>)B);
    111. }
    112. private void balanceRL(TreeNode<E> A, TreeNode<E> parentOfA){
    113. TreeNode<E> B = A.right;
    114. TreeNode<E> C = B.left;
    115. if(A == root){
    116. root = C;
    117. }else{
    118. if(parentOfA.left == A){
    119. parentOfA.left = C;
    120. }else{
    121. parentOfA.right = C;
    122. }
    123. }
    124. A.right = C.left;
    125. B.left = C.right;
    126. C.left = A;
    127. C.right = B;
    128. updateHeight((AVLTreeNode<E>)A);
    129. updateHeight((AVLTreeNode<E>)B);
    130. updateHeight((AVLTreeNode<E>)C);
    131. }
    132. // 和BST中的删除方法一样,只不过需要判断下是否平衡
    133. @Override
    134. public boolean delete(E element){
    135. if (root == null) {
    136. return false;
    137. }
    138. TreeNode<E> parent = null;
    139. TreeNode<E> current = root;
    140. while(current != null){
    141. if(element.compareTo(current.element) < 0){
    142. parent = current;
    143. current = current.left;
    144. }else if(element.compareTo(current.element) > 0){
    145. parent = current;
    146. current = current.right;
    147. }else break;
    148. }
    149. if(current == null){
    150. return false;
    151. }
    152. if(current.left == null){
    153. if(parent == null){
    154. root = current.right;
    155. }else {
    156. if(element.compareTo(parent.element) < 0){
    157. parent.left = current.right;
    158. }else{
    159. parent.right = current.right;
    160. }
    161. balancePath(parent.element);
    162. }
    163. }else{
    164. TreeNode<E> parentOfRightMost = current;
    165. TreeNode<E> rightMost = current.left;
    166. while(rightMost.right != null){
    167. parentOfRightMost = rightMost;
    168. rightMost = rightMost.right;
    169. }
    170. current.element = rightMost.element;
    171. if(parentOfRightMost.right == rightMost){
    172. parentOfRightMost.right = rightMost.left;
    173. }else{
    174. parentOfRightMost.left = rightMost.left;
    175. }
    176. balancePath(parentOfRightMost.element);
    177. }
    178. size--;
    179. return true;
    180. }
    181. protected static class AVLTreeNode<E> extends BST.TreeNode<E>{
    182. protected int height = 0;
    183. public AVLTreeNode(E e){
    184. super(e);
    185. }
    186. }
    187. }

    四、测试AVLTree类

    1. public class TestAVLTree {
    2. public static void main(String[] args){
    3. AVLTree<Integer> tree = new AVLTree<Integer>(new Integer[]{25, 20 ,5});
    4. System.out.print("After inserting 25,20,,5: ");
    5. printTree(tree);
    6. tree.insert(34);
    7. tree.insert(50);
    8. System.out.print("\nAfter inserting 34, 50 : ");
    9. printTree(tree);
    10. tree.insert(30);
    11. System.out.print("\nAfter inserting 30 : ");
    12. printTree(tree);
    13. tree.insert(10);
    14. System.out.print("\nAfter inserting 10 : ");
    15. printTree(tree);
    16. tree.delete(34);
    17. tree.delete(30);
    18. tree.delete(50);
    19. System.out.print("\nAfter removing 34, 30, 50: ");
    20. printTree(tree);
    21. tree.delete(5);
    22. System.out.print("\nAfter removing 5 : ");
    23. printTree(tree);
    24. System.out.print("\nTraverse the element in the tree: ");
    25. for(Object e : tree){
    26. System.out.print(e + " ");
    27. }
    28. }
    29. public static void printTree(BST tree){
    30. System.out.print("\nInorder (sorted) : ");
    31. tree.inorder();
    32. System.out.print("\nPostorder : ");
    33. tree.postorder();
    34. System.out.print("\nPreorder");
    35. tree.preorder();
    36. System.out.print("\nThe number of nodes is " + tree.getSize());
    37. System.out.println();
    38. }
    39. }

    程序插入图示: 

     程序删除图示:

  • 相关阅读:
    苹果注定要输给欧盟,USB-C成为标准接口已是大势所趋
    使用 GitHub Actions 通过 CI/CD 简化 Flutter 应用程序开发
    git常用命令
    Ubuntu 22.04 无法使用网易云音乐
    ecology集成中心hr同步的增量标识的意思
    【区块链 | Compound】3.剖析DeFi借贷产品之Compound:Subgraph篇
    WLAN 无线案例(华为AC控制器配置模板)
    第8天:可变与不可变类型、字典、元组与集合的内置方法
    基于FME Desktop和FME Server的数据增量自动更新
    [附源码]Python计算机毕业设计Django教学辅助系统
  • 原文地址:https://blog.csdn.net/Yan__Ran/article/details/125513921