手寫AVL平衡二叉搜尋樹

二叉搜尋樹的侷限性

先說一下什麼是二叉搜尋樹,二叉樹每個節點只有兩個節點,二叉搜尋樹的每個左子節點的值小於其父節點的值,每個右子節點的值大於其左子節點的值。如下圖:

二叉搜尋樹,顧名思義,它的搜尋效率很高,可以達到O(logn)。但這是理想狀況下的,即上圖所示。實際上,由於插入順序的原因,形成的二叉搜尋樹並不會像上圖這樣“工整”,最壞的情況的下,甚至可能會退化成連結串列了,如下圖:

這顯然不是我們想要看的結果,那麼我們必須要引入一套機制來避免這種事情的發生,也就是讓二叉搜尋樹帶上平衡條件。

AVL平衡二叉搜尋樹

幾個基本概念

  • 葉子節點:既沒有左子節點,也沒有右左子節點的節點就是葉子節點。

  • 樹的高度:葉子節點的高度為1,空節點的高度是-1,父節點的高度是其兩個子樹較高一棵子樹的高度加一。

  • 平衡條件:每一個節點的左子樹與右子樹的高度差不超過1。

核心思想

因為AVL平衡二叉搜尋樹,父節點的兩顆子樹的高度差不能超過1。在AVL平衡二叉樹種,採用旋轉的機制來使不滿足平衡條件的二叉樹重新回到滿足平衡條件的狀態。在二叉搜尋樹中,需要被平衡的情況可以分為兩大類總共四種情況,

  • 單旋轉

    • 左旋轉
    • 右旋轉
  • 雙旋轉
    • 先左旋,再右旋
    • 先右旋,再左旋

如下圖所示:

通過圖片的形式我們很容易就可以寫出使二叉樹回到滿足平衡條件的程式碼

  1. // 右旋轉
  2. public TreeNode rightRotate(TreeNode root) {
  3. TreeNode temp1 = root.left;
  4. TreeNode temp2 = temp1.right;
  5. temp1.right = root;
  6. root.left = temp2;
  7. return temp1;
  8. }
  9. // 左旋轉
  10. public TreeNode leftRotate(TreeNode root) {
  11. TreeNode temp1 = root.right;
  12. TreeNode temp2 = temp1.left;
  13. temp1.left = root;
  14. root.right = temp2;
  15. return temp1;
  16. }
  17. // 先右後左
  18. public TreeNode rightLeftRotate(TreeNode root) {
  19. root.right = rightRotate(root.right);
  20. return leftRotate(root);
  21. }
  22. // 先左後右
  23. public TreeNode leftRightRotate(TreeNode root) {
  24. root.left = leftRotate(root.left);
  25. return rightRotate(root);
  26. }

我們必須再每一次插入節點後判斷樹是否需要平衡,也就是是否會出現兩顆子樹的高度差超過1的情況,首先編寫一個可以計算出傳入節點 高度的函式。

  1. public int height(TreeNode root) {
  2. if (root == null) {
  3. return -1;
  4. }
  5. if (root.left == null && root.right == null) {
  6. return 0;
  7. }
  8. return Math.max(height(root.right), height(root.left));
  9. }

有了這個函式,我們就不僅可以判斷是否出現需要平衡的情況,還可以判斷需要平衡的情況是四種情況種的哪一種。

  1. public TreeNode balance(TreeNode root) {
  2. int l = height(root.left);
  3. int r = height(root.right);
  4. if (l - r >= 2) {
  5. // rightRotate
  6. if (height(root.left.left) - height(root.left.right) >= 1) {
  7. // rightRotate
  8. root = rightRotate(root);
  9. } else if (height(root.left.right) - height(root.left.left) >= 1) {
  10. // leftRightRotate
  11. root = leftRightRotate(root);
  12. }
  13. } else if (r - l >= 2) {
  14. // leftRotate
  15. if (height(root.right.right) - height(root.right.left) >= 1) {
  16. // leftRotate
  17. root = leftRotate(root);
  18. } else if (height(root.right.left) - height(root.right.right) >= 1){
  19. root = rightLeftRotate(root);
  20. }
  21. }
  22. return root;
  23. }

以上就是AVL平衡二叉搜尋樹的精髓,並且已經用程式碼實現了。

完整程式碼

這是我完善後的功能相對完整的AVL二叉搜尋平衡樹。

  1. class TreeNode {
  2. int value;
  3. TreeNode left;
  4. TreeNode right;
  5. int height;
  6. public TreeNode(int value) {
  7. this.value = value;
  8. }
  9. }
  10. public class AVLBinarySearchTree {
  11. public static void main(String[] args) {
  12. AVLBinarySearchTree a = new AVLBinarySearchTree();
  13. for (int i = 0; i < 10; i++) {
  14. a.insert(i);
  15. }
  16. }
  17. private TreeNode root;
  18. private static final int ALLOWED_IMBALANCE = 1;
  19. // 刪除元素
  20. public void remove(int value) {
  21. root = remove(value, root);
  22. }
  23. // 檢查是否包含某一元素,包含則返回該節點,不包含則返回null
  24. public TreeNode contain(int value) {
  25. TreeNode temp = root;
  26. if (temp == null) {
  27. return temp;
  28. }
  29. while (temp.value != value) {
  30. if (value > temp.value) {
  31. temp = temp.right;
  32. } else if (value < temp.value) {
  33. temp = temp.left;
  34. }
  35. }
  36. return temp;
  37. }
  38. // 刪除指定子樹上的指定元素
  39. private TreeNode remove(int value, TreeNode abn) {
  40. if (abn == null) {
  41. return abn;
  42. }
  43. if (value > abn.value) {
  44. abn.right = remove(value, abn.right);
  45. } else if (value < abn.value) {
  46. abn.left = remove(value, abn.left);
  47. } else {
  48. if (abn.right == null && abn.left == null) {
  49. abn = null;
  50. return abn;
  51. } else if (abn.right != null) {
  52. abn.value = findMin(abn.right).value;
  53. abn.right = remove(abn.value, abn.right);
  54. } else {
  55. abn.value = findMax(abn.left).value;
  56. abn.left = remove(abn.value, abn.left);
  57. }
  58. }
  59. return balance(abn);
  60. }
  61. // 找到指定子樹最大值
  62. private TreeNode findMax(TreeNode abn) {
  63. if (abn == null) {
  64. return null;
  65. }
  66. TreeNode temp = abn;
  67. while (temp.right != null) {
  68. temp = temp.right;
  69. }
  70. return temp;
  71. }
  72. // 找到指定子樹最小值
  73. private TreeNode findMin(TreeNode abn) {
  74. if (abn == null) {
  75. return null;
  76. }
  77. TreeNode temp = abn;
  78. while (temp.left != null) {
  79. temp = temp.left;
  80. }
  81. return temp;
  82. }
  83. // 插入節點
  84. public void insert(int value) {
  85. root = insert(value, root);
  86. }
  87. // 計算節點高度
  88. private int height(TreeNode abn) {
  89. if (abn == null) {
  90. return -1;
  91. }
  92. return abn.height;
  93. }
  94. // 樹的高度
  95. public int height() {
  96. return height(root);
  97. }
  98. // 插入節點
  99. private TreeNode insert(int value, TreeNode abn) {
  100. if (abn == null) {
  101. return new TreeNode(value);
  102. }
  103. if (value > abn.value) {
  104. abn.right = insert(value, abn.right);
  105. } else if (value < abn.value) {
  106. abn.left = insert(value, abn.left);
  107. }
  108. return balance(abn);
  109. }
  110. // 平衡不平衡的樹
  111. private TreeNode balance(TreeNode abn) {
  112. if (height(abn.left) - height(abn.right) > ALLOWED_IMBALANCE) {
  113. if (height(abn.left.left) >= height(abn.left.right)) {
  114. abn = leftSingleRotate(abn);
  115. } else if (height(abn.left.left) < height(abn.left.right)) {
  116. abn = leftDoubleRotate(abn);
  117. }
  118. } else if (height(abn.right) - height(abn.left) > ALLOWED_IMBALANCE) {
  119. if (height(abn.right.right) >= height(abn.right.left)) {
  120. abn = rightSingleRotate(abn);
  121. } else {
  122. abn = rightDoubleRotate(abn);
  123. }
  124. }
  125. abn.height = Math.max(height(abn.left), height(abn.right)) + 1;
  126. return abn;
  127. }
  128. // 右單旋轉
  129. private TreeNode rightSingleRotate(TreeNode abn) {
  130. TreeNode temp = abn;
  131. abn = abn.right;
  132. temp.right = abn.left;
  133. abn.left = temp;
  134. temp.height = Math.max(height(temp.right), height(temp.left)) + 1;
  135. abn.height = Math.max(height(abn.right), temp.height) + 1;
  136. return abn;
  137. }
  138. // 左單旋轉
  139. private TreeNode leftSingleRotate(TreeNode abn) {
  140. TreeNode temp = abn;
  141. abn = abn.left;
  142. temp.left = abn.right;
  143. abn.right = temp;
  144. temp.height = Math.max(height(temp.right), height(temp.left)) + 1;
  145. abn.height = Math.max(height(abn.right), temp.height) + 1;
  146. return abn;
  147. }
  148. // 右雙旋轉
  149. private TreeNode rightDoubleRotate(TreeNode abn) {
  150. abn.right = leftSingleRotate(abn.right);
  151. return rightSingleRotate(abn);
  152. }
  153. // 左雙旋轉
  154. private TreeNode leftDoubleRotate(TreeNode abn) {
  155. abn.left = rightSingleRotate(abn.left);
  156. return leftSingleRotate(abn);
  157. }
  158. }