• 平衡二叉树相关概念以及性质
  • 平衡二叉树的类结构以及简单处理方法
  • 获取树的高度以及计算平衡因子
  • 判断树是不是BSTAVL
  • LL型失衡以及右旋转
  • RR型失衡以及左旋转
  • LR型失衡以及处理方法
  • RL型失衡以及处理方法
  • add()remove()中失衡处理调整
  • 完整源码测试
  • 使用LeetCode-350. Intersection of Two Arrays II测试代码

一、前言

  • 在学二叉平衡树之前,可以先学一下二叉排序树
  • 如果对于四种旋转,实在想不清的可以看一下这个动态视频

二、平衡二叉树相关概念以及性质

相关基本概念:

  • 平衡二叉树是一种二叉排序树,:要么是一棵空树,要么左右都是平衡二叉树,且左子树和右子树深度之绝对值不超过1. 将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF,那么平衡二叉树上的所有结点的平衡因子只可能是-101。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
  • 距离插入结点最近的,且平衡因子的绝对值大于1的结点为根的子树,称为最小不平衡子树。其中我们可以记住两个名词, 发现者:即第一个发现这个结点两边的高度之差大于1(我们也叫做最小不平衡结点 : 距离插入节点最近的不平衡节点)。破坏者:即插入这个结点之后使得树不平衡的那个点。(看等下旋转的例子就知道)。

三、平衡二叉树的类结构以及简单处理方法

二叉平衡树和二叉排序树的结构定义差不多,这里增加了一个hegiht属性,表示每一个结点为根的子树的高度。
其中大部分方法已经在二叉搜索树集合和映射中解释和实现过。

  1. /**
  2. * 基于BST实现的AVL
  3. */
  4. public class AVLTree<K extends Comparable<K>,V> {
  5. private class Node{
  6. public K key;
  7. public V value;
  8. public Node left,right;
  9. public int height; //每一个结点都要记录一下高度 --> 为了求出每个结点的平衡因子
  10. public Node(K key, V value) {
  11. this.key = key;
  12. this.value = value;
  13. this.left = null;
  14. this.right = null;
  15. height = 1; //默认的每个新结点(叶子结点)高度都是1
  16. }
  17. }
  18. private Node root;
  19. private int size;
  20. public AVLTree(){
  21. root = null;
  22. size = 0;
  23. }
  24. public int size(){
  25. return size;
  26. }
  27. public boolean isEmpty(){
  28. return size == 0;
  29. }
  30. //返回以node为根节点的二分搜索树中,key所在的结点
  31. public Node getNode(Node node,K key){
  32. if(node == null)
  33. return null;
  34. if(key.compareTo(node.key) == 0){
  35. return node;
  36. }else if(key.compareTo(node.key) < 0) {
  37. return getNode(node.left,key);
  38. }else {
  39. return getNode(node.right,key);
  40. }
  41. }
  42. public boolean contains(K key){
  43. return getNode(root,key) != null;
  44. }
  45. public V get(K key){
  46. Node node = getNode(root,key);
  47. return node == null ? null : node.value;
  48. }
  49. public void set(K key,V newValue){
  50. Node node = getNode(root,key);
  51. if(node == null)
  52. throw new IllegalArgumentException(key + " doesn't exist !");
  53. node.value = newValue;
  54. }
  55. // 返回以node 为根的二分搜索树 最小值所在的结点
  56. private Node minumum(Node node){
  57. if(node.left == null)
  58. return node;
  59. return minumum(node.left);
  60. }
  61. //移除以node为根的二叉搜索树中最小值的结点,返回删除结点之后新的二叉搜索树的根
  62. private Node removeMin(Node node){
  63. if(node.left == null){
  64. Node rightNode = node.right;
  65. node.right = null;
  66. size--;
  67. return rightNode;
  68. }
  69. node.left = removeMin(node.left); //递归 and connect 例如下面返回一个rightNode, 我的left就连接上它
  70. return node;
  71. }
  72. }

三、获取树的高度以及计算平衡因子

这两个方法很简单,平衡因子就是 左子树高度 - 右子树高度。

  1.   private int getHeight(Node node){
  2. if(node == null)return 0; //空树的高度是0
  3. return node.height;
  4. }
  5. //计算平衡因子 : 左子树高度-右子树高度
  6. private int getBalanceFactor(Node node){
  7. if(node == null)return 0;
  8. return getHeight(node.left) - getHeight(node.right);
  9. }

四、判断树是不是BST和AVL

这两个方法我在这篇博客这篇博客中也写过,这里判断BST使用的是递归,原理都是利用了中序遍历和BST的性质。判断平衡二叉树就更简单了。

  1. //判断一棵树是不是二叉搜索树
  2. private boolean isBST(){
  3. ArrayList<K>keys = new ArrayList<>();
  4. inOrder(root,keys);
  5. for(int i = 1; i < keys.size(); i++){
  6. if(keys.get(i-1).compareTo(keys.get(i)) > 0)return false;
  7. }
  8. return true;
  9. }
  10. //递归中序
  11. private void inOrder(Node node, ArrayList<K> keys) {
  12. if(node == null )return;
  13. inOrder(node.left,keys);
  14. keys.add(node.key);
  15. inOrder(node.right,keys);
  16. }
  1. //判断这颗二叉树是不是平衡二叉树
  2. private boolean isBalanced(){
  3. return isBalanced(root);
  4. }
  5. private boolean isBalanced(Node node) { // self is balance and child is balance
  6. if(node == null)
  7. return true; // empty tree is a balance tree
  8. if(Math.abs(getBalanceFactor(node)) > 1)
  9. return false;
  10. return isBalanced(node.left) && isBalanced(node.right);
  11. }

五、LL型失衡以及右旋转

LL失衡就是破坏者是发现者的左孩子的左孩子 :

平衡二叉树总结 - 图1

解决的办法:

  • ① 先将T3(x的右孩子结点或者子树)扔到一边;
  • ② 将以y为根的树顺时针旋转下来,接到x的右孩子;
  • ③ 然后将T3放到y的左孩子地方;
  • ④ 调整完之后记得更新高度;

平衡二叉树总结 - 图2

① 先将T3(x的右孩子结点或者子树)扔到一边:

平衡二叉树总结 - 图3

② 将以y为根的树顺时针旋转下来,接到x的右孩子:

平衡二叉树总结 - 图4

③ 然后将T3放到y的左孩子地方:

平衡二叉树总结 - 图5

更新的前后关系如下:

平衡二叉树总结 - 图6

代码很简单:

  1. /** 对节点y进行向右旋转操作,返回旋转后新的根节点x
  2. 右旋转 y x
  3. / \ / \
  4. x T4 向右旋转 (y) z y
  5. / \ - - - - - - - -> / \ / \
  6. z T3 T1 T2 T3 T4
  7. / \
  8. T1 T2
  9. */
  10. private Node rightRotate(Node y){ // y是失衡点
  11. Node x = y.left;
  12. Node T3 = x.right;
  13. x.right = y;
  14. y.left = T3;
  15. //调整之后需要更新height 注意要先更新y的height
  16. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  17. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  18. return x;
  19. }

六、RR型失衡以及左旋转

RR型失衡和LL型失衡对应,破坏者是发现者的右孩子的右孩子:

平衡二叉树总结 - 图7
平衡二叉树总结 - 图8
平衡二叉树总结 - 图9

代码:

  1. /** 对节点y进行向左旋转操作,返回旋转后新的根节点x
  2. y x
  3. / \ / \
  4. T4 x 向左旋转 (y) y z
  5. / \ - - - - - - - -> / \ / \
  6. T3 z T4 T3 T1 T2
  7. / \
  8. T1 T2
  9. */
  10. private Node leftRotate(Node y){
  11. Node x = y.right;
  12. Node T3 = x.left;
  13. x.left = y;
  14. y.right = T3;
  15. //更新height
  16. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  17. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  18. return x;
  19. }

七、LR型失衡以及处理方法

LR型就是破坏者是node的左孩子的右孩子。处理方法要分为两部:

  • 先对node的左孩子进行左旋转,变成LL型;
  • 然后对node自己进行右旋转即可

平衡二叉树总结 - 图10
平衡二叉树总结 - 图11
平衡二叉树总结 - 图12


八、RL型失衡以及处理方法

同理,和LR型对称的,RL型就是破坏者是node的右孩子的左孩子。处理方法:

  • 先对node.right进行右旋转(rightRotate)变成RR型;
  • 然后对自己进行左旋转即可。

平衡二叉树总结 - 图13
平衡二叉树总结 - 图14


九、add()remove()中失衡处理调整

我们在插入或者移除元素时有可能会破坏二叉树的平衡性,所以需要调整,调整步骤:

  • 先更新height
  • 计算平衡因子
  • 判断失衡类型

其中判断失衡类型总共有四种 :

假设int balanceFactor = getBalanceFactor(node);也就是balanceFactornode的平衡因子。

  • LL型,balanceFactor > 1 && getBalanceFactor(node.left) >= 0;
  • RR型,balanceFactor < -1 && getBalanceFactor(node.right) <= 0;
  • LR型,balanceFactor > 1 && getBalanceFactor(node.left) < 0;
  • RL型,balanceFactor < -1 && getBalanceFactor(node.right) > 0;

其中add()添加元素和二叉搜索树区别不大,就是这里存储的是键值对<K,V>映射,而之前的二叉搜索树存储的是集合,相当于JDK中TreeSetTreeMap一样的区别。

  1. //向AVL树中添加新的元素(key,valu)
  2. public void add(K key,V value){
  3. root = add(root,key,value);
  4. }
  5. private Node add(Node node,K key,V value){
  6. if(node == null){
  7. size++;
  8. return new Node(key, value); //新建默认高度是height = 1
  9. }
  10. if(key.compareTo(node.key) < 0){
  11. node.left = add(node.left,key,value);
  12. }else if(key.compareTo(node.key) > 0){
  13. node.right = add(node.right,key,value);
  14. }else { // update
  15. node.value = value;
  16. }
  17. /** 1) 更新height */
  18. node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
  19. /** 2)计算平衡因子 */
  20. int balanceFactor = getBalanceFactor(node);
  21. /** 3)调整 : 分为四种情况下的调整*/
  22. /** LL型 */
  23. if(balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
  24. return rightRotate(node);
  25. /** RR型 */
  26. if(balanceFactor < -1 && getBalanceFactor(node.right) <= 0){
  27. return leftRotate(node);
  28. }
  29. /** LR型 */
  30. if(balanceFactor > 1 && getBalanceFactor(node.left) < 0){
  31. node.left = leftRotate(node.left); //左孩子先进行左旋转
  32. return rightRotate(node); //自己再进行右旋转
  33. }
  34. /** RL型 */
  35. if(balanceFactor < -1 && getBalanceFactor(node.right) > 0){
  36. node.right = rightRotate(node.right); //右孩子先进行右旋转
  37. return leftRotate(node); //自己再进行左旋转
  38. }
  39. return node;
  40. }

remove方法和之前的二叉搜索树中实现的remove方法稍微有点不同:

  • 因为我们在remove之后要调整平衡,所以使用了一个retNode来存储各种操作之后要返回的node,最后统一调整
  • 在调整之前判断一下retNode是否为null,如果是null的话不能操作,否则抛出空指针异常
  • 有一个bug,如果还是使用之前的removeMin来处理在删除一个左右孩子都全的node的时候,在removeMin中没有进行平衡的调整,所以我们在node.right中删除successor的时候使用的是递归的删除,也就是调用自己的remove方法
  • key.compareTo(node.key) == 0的时候,也就是else 中要删除node的三个逻辑要互斥的处理,不然会重复,因为不是直接返回,和二叉搜索树的处理不同,要注意
  1. public V remove(K key){
  2. Node node = getNode(root,key);
  3. if(node != null){
  4. root = remove(root,key);
  5. return node.value;
  6. }
  7. return null;
  8. }
  9. private Node remove(Node node, K key){
  10. if(node == null)return null;
  11. Node retNode = null;
  12. if(key.compareTo(node.key) < 0){
  13. node.left = remove(node.left,key);
  14. retNode = node;
  15. }else if(key.compareTo(node.key) > 0){
  16. node.right = remove(node.right,key);
  17. retNode = node;
  18. }else {
  19. //和BST不同 三种情况是一个互斥的关系
  20. if(node.left == null){
  21. Node rightNode = node.right;
  22. node.right = null;
  23. size--;
  24. retNode = rightNode;
  25. }else if(node.right == null){
  26. Node leftNode = node.left;
  27. node.left = null;
  28. size--;
  29. retNode = leftNode;
  30. }else {
  31. Node successor = minumum(node.right);
  32. // successor.right = removeMin(node.right); //这里和BST 不同,这里有可能会破坏平衡,所以不用这个
  33. successor.right = remove(node.right,successor.key); /**这里自己可以维护平衡*/
  34. successor.left = node.left;
  35. node.right = node.left = null;
  36. retNode = successor;
  37. }
  38. }
  39. /**特判 和 BST不同*/
  40. if(retNode == null)return null;
  41. /** 1) 更新height */
  42. retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
  43. /** 2)计算平衡因子 */
  44. int balanceFactor = getBalanceFactor(retNode);
  45. /** 3)调整 : 分为四种情况下的调整*/
  46. /** LL型 */
  47. if(balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
  48. return rightRotate(retNode);
  49. /** RR型 */
  50. if(balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0){
  51. return leftRotate(retNode);
  52. }
  53. /** LR型 */
  54. if(balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){
  55. retNode.left = leftRotate(retNode.left); //左孩子先进行左旋转
  56. return rightRotate(retNode); //自己再进行右旋转
  57. }
  58. /** RL型 */
  59. if(balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){
  60. retNode.right = rightRotate(retNode.right); //右孩子先进行右旋转
  61. return leftRotate(retNode); //自己再进行左旋转
  62. }
  63. return retNode;
  64. }

十、完整源码测试

  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. /**
  4. * 基于BST实现的AVL
  5. */
  6. public class AVLTree<K extends Comparable<K>,V> {
  7. private class Node{
  8. public K key;
  9. public V value;
  10. public Node left,right;
  11. public int height; //每一个结点都要记录一下高度 --> 为了求出每个结点的平衡因子
  12. public Node(K key, V value) {
  13. this.key = key;
  14. this.value = value;
  15. this.left = null;
  16. this.right = null;
  17. height = 1; //默认的每个新结点(叶子结点)高度都是1
  18. }
  19. }
  20. private Node root;
  21. private int size;
  22. public AVLTree(){
  23. root = null;
  24. size = 0;
  25. }
  26. public int size(){
  27. return size;
  28. }
  29. public boolean isEmpty(){
  30. return size == 0;
  31. }
  32. private int getHeight(Node node){
  33. if(node == null)return 0; //空树的高度是0
  34. return node.height;
  35. }
  36. //计算平衡因子 : 左子树高度-右子树高度
  37. private int getBalanceFactor(Node node){
  38. if(node == null)return 0;
  39. return getHeight(node.left) - getHeight(node.right);
  40. }
  41. //判断一棵树是不是二叉搜索树
  42. private boolean isBST(){
  43. ArrayList<K>keys = new ArrayList<>();
  44. inOrder(root,keys);
  45. for(int i = 1; i < keys.size(); i++){
  46. if(keys.get(i-1).compareTo(keys.get(i)) > 0)return false;
  47. }
  48. return true;
  49. }
  50. private void inOrder(Node node, ArrayList<K> keys) {
  51. if(node == null )return;
  52. inOrder(node.left,keys);
  53. keys.add(node.key);
  54. inOrder(node.right,keys);
  55. }
  56. //判断这颗二叉树是不是平衡二叉树
  57. private boolean isBalanced(){
  58. return isBalanced(root);
  59. }
  60. private boolean isBalanced(Node node) { // self is balance and child is balance
  61. if(node == null)return true; // empty tree is a balance tree
  62. if(Math.abs(getBalanceFactor(node)) > 1)return false;
  63. return isBalanced(node.left) && isBalanced(node.right);
  64. }
  65. /** 对节点y进行向右旋转操作,返回旋转后新的根节点x
  66. 右旋转 y x
  67. / \ / \
  68. x T4 向右旋转 (y) z y
  69. / \ - - - - - - - -> / \ / \
  70. z T3 T1 T2 T3 T4
  71. / \
  72. T1 T2
  73. */
  74. private Node rightRotate(Node y){ // y是失衡点
  75. Node x = y.left;
  76. Node T3 = x.right;
  77. x.right = y;
  78. y.left = T3;
  79. //调整之后需要更新height 注意要先更新y的height
  80. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  81. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  82. return x;
  83. }
  84. /** 对节点y进行向左旋转操作,返回旋转后新的根节点x
  85. y x
  86. / \ / \
  87. T4 x 向左旋转 (y) y z
  88. / \ - - - - - - - -> / \ / \
  89. T3 z T4 T3 T1 T2
  90. / \
  91. T1 T2
  92. */
  93. private Node leftRotate(Node y){
  94. Node x = y.right;
  95. Node T3 = x.left;
  96. x.left = y;
  97. y.right = T3;
  98. //更新height
  99. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  100. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  101. return x;
  102. }
  103. //向AVL树中添加新的元素(key,valu)
  104. public void add(K key,V value){
  105. root = add(root,key,value);
  106. }
  107. private Node add(Node node,K key,V value){
  108. if(node == null){
  109. size++;
  110. return new Node(key, value); //新建默认高度是height = 1
  111. }
  112. if(key.compareTo(node.key) < 0){
  113. node.left = add(node.left,key,value);
  114. }else if(key.compareTo(node.key) > 0){
  115. node.right = add(node.right,key,value);
  116. }else { // update
  117. node.value = value;
  118. }
  119. /** 1) 更新height */
  120. node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
  121. /** 2)计算平衡因子 */
  122. int balanceFactor = getBalanceFactor(node);
  123. /** 3)调整 : 分为四种情况下的调整*/
  124. /** LL型 */
  125. if(balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
  126. return rightRotate(node);
  127. /** RR型 */
  128. if(balanceFactor < -1 && getBalanceFactor(node.right) <= 0){
  129. return leftRotate(node);
  130. }
  131. /** LR型 */
  132. if(balanceFactor > 1 && getBalanceFactor(node.left) < 0){
  133. node.left = leftRotate(node.left); //左孩子先进行左旋转
  134. return rightRotate(node); //自己再进行右旋转
  135. }
  136. /** RL型 */
  137. if(balanceFactor < -1 && getBalanceFactor(node.right) > 0){
  138. node.right = rightRotate(node.right); //右孩子先进行右旋转
  139. return leftRotate(node); //自己再进行左旋转
  140. }
  141. return node;
  142. }
  143. //返回以node为根节点的二分搜索树中,key所在的结点
  144. public Node getNode(Node node,K key){
  145. if(node == null)
  146. return null;
  147. if(key.compareTo(node.key) == 0){
  148. return node;
  149. }else if(key.compareTo(node.key) < 0) {
  150. return getNode(node.left,key);
  151. }else {
  152. return getNode(node.right,key);
  153. }
  154. }
  155. public boolean contains(K key){
  156. return getNode(root,key) != null;
  157. }
  158. public V get(K key){
  159. Node node = getNode(root,key);
  160. return node == null ? null : node.value;
  161. }
  162. public void set(K key,V newValue){
  163. Node node = getNode(root,key);
  164. if(node == null)
  165. throw new IllegalArgumentException(key + " doesn't exist !");
  166. node.value = newValue;
  167. }
  168. // 返回以node 为根的二分搜索树 最小值所在的结点
  169. private Node minumum(Node node){
  170. if(node.left == null)
  171. return node;
  172. return minumum(node.left);
  173. }
  174. //移除以node为根的二叉搜索树中最小值的结点,返回删除结点之后新的二叉搜索树的根
  175. private Node removeMin(Node node){
  176. if(node.left == null){
  177. Node rightNode = node.right;
  178. node.right = null;
  179. size--;
  180. return rightNode;
  181. }
  182. node.left = removeMin(node.left); //递归 and connect 例如下面返回一个rightNode, 我的left就连接上它
  183. return node;
  184. }
  185. public V remove(K key){
  186. Node node = getNode(root,key);
  187. if(node != null){
  188. root = remove(root,key);
  189. return node.value;
  190. }
  191. return null;
  192. }
  193. private Node remove(Node node, K key){
  194. if(node == null)return null;
  195. Node retNode = null;
  196. if(key.compareTo(node.key) < 0){
  197. node.left = remove(node.left,key);
  198. retNode = node;
  199. }else if(key.compareTo(node.key) > 0){
  200. node.right = remove(node.right,key);
  201. retNode = node;
  202. }else {
  203. //和BST不同 三种情况是一个互斥的关系
  204. if(node.left == null){
  205. Node rightNode = node.right;
  206. node.right = null;
  207. size--;
  208. retNode = rightNode;
  209. }else if(node.right == null){
  210. Node leftNode = node.left;
  211. node.left = null;
  212. size--;
  213. retNode = leftNode;
  214. }else {
  215. Node successor = minumum(node.right);
  216. // successor.right = removeMin(node.right); //这里和BST 不同,这里有可能会破坏平衡,所以不用这个
  217. successor.right = remove(node.right,successor.key); /**这里自己可以维护平衡*/
  218. successor.left = node.left;
  219. node.right = node.left = null;
  220. retNode = successor;
  221. }
  222. }
  223. /**特判 和 BST不同*/
  224. if(retNode == null)return null;
  225. /** 1) 更新height */
  226. retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
  227. /** 2)计算平衡因子 */
  228. int balanceFactor = getBalanceFactor(retNode);
  229. /** 3)调整 : 分为四种情况下的调整*/
  230. /** LL型 */
  231. if(balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
  232. return rightRotate(retNode);
  233. /** RR型 */
  234. if(balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0){
  235. return leftRotate(retNode);
  236. }
  237. /** LR型 */
  238. if(balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){
  239. retNode.left = leftRotate(retNode.left); //左孩子先进行左旋转
  240. return rightRotate(retNode); //自己再进行右旋转
  241. }
  242. /** RL型 */
  243. if(balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){
  244. retNode.right = rightRotate(retNode.right); //右孩子先进行右旋转
  245. return leftRotate(retNode); //自己再进行左旋转
  246. }
  247. return retNode;
  248. }
  249. public void printTree(){
  250. printTree(root,0,"H",8);
  251. }
  252. public void printTree(Node head,int height,String to,int len){
  253. if(head == null)return;
  254. printTree(head.right,height + 1,"v",len);
  255. String val = to + head.key + to; //两边指示的字符
  256. int lenV = val.length();
  257. int lenL = (len - lenV)/2; //左边的空格(分一半)
  258. int lenR = len - lenV - lenL; // 右边的空格
  259. System.out.println( getSpace(len * height) + getSpace(lenL) + val + getSpace(lenR));
  260. printTree(head.left,height + 1,"^",len);
  261. }
  262. public static String getSpace(int len){
  263. StringBuffer str = new StringBuffer();
  264. for(int i = 0; i < len; i++) str.append(" ");
  265. return str.toString();
  266. }
  267. /**
  268. * for test
  269. */
  270. public static void main(String[] args) {
  271. Integer[] arr = {21,14,28,11,18,25,32,5,12,15,19,23,27,30,37};
  272. Arrays.sort(arr);
  273. AVLTree<Integer,Integer>avlTree = new AVLTree<>();
  274. for(int i = 0; i < arr.length; i++) avlTree.add(arr[i],null);
  275. avlTree.printTree();
  276. System.out.println(avlTree.isBalanced());
  277. System.out.println(avlTree.isBST());
  278. }
  279. }

效果
平衡二叉树总结 - 图15

  • 打印二叉树见这个博客
  • 值得注意的是,我在插入之前对arr进行了排序,如果是BST会退化成链表,如下图所示,但是这里的AVL不会;

平衡二叉树总结 - 图16


十一、使用LeetCode-350. Intersection of Two Arrays II测试代码

为了保证AVLTree的正确性,使用LeetCode-350测试:

题目链接

题目

平衡二叉树总结 - 图17

解析

使用 HashMapTreeMap都能很简单的做出来,这里使用自己写的AVLTree测试一下:

  1. import java.util.ArrayList;
  2. class Solution {
  3. public int[] intersect(int[] nums1, int[] nums2) {
  4. AVLTree<Integer,Integer> map = new AVLTree<>();
  5. for(int num : nums1){
  6. if(!map.contains(num)) {
  7. map.add(num,1);
  8. }else {
  9. map.add(num, map.get(num) + 1);
  10. }
  11. }
  12. ArrayList<Integer>list = new ArrayList<>();
  13. for(int num : nums2){
  14. if(map.contains(num)){
  15. list.add(num);
  16. map.add(num,map.get(num) - 1);
  17. if(map.get(num) == 0)
  18. map.remove(num);
  19. }
  20. }
  21. int[] res = new int[list.size()];
  22. for(int i = 0; i < list.size(); i++){
  23. res[i] = list.get(i);
  24. }
  25. return res;
  26. }
  27. /**
  28. * 基于BST实现的AVL
  29. */
  30. private class AVLTree<K extends Comparable<K>,V> {
  31. private class Node{
  32. public K key;
  33. public V value;
  34. public Node left,right;
  35. public int height; //每一个结点都要记录一下高度 --> 为了求出每个结点的平衡因子
  36. public Node(K key, V value) {
  37. this.key = key;
  38. this.value = value;
  39. this.left = null;
  40. this.right = null;
  41. height = 1; //默认的每个新结点(叶子结点)高度都是1
  42. }
  43. }
  44. private Node root;
  45. private int size;
  46. public AVLTree(){
  47. root = null;
  48. size = 0;
  49. }
  50. public int size(){
  51. return size;
  52. }
  53. public boolean isEmpty(){
  54. return size == 0;
  55. }
  56. private int getHeight(Node node){
  57. if(node == null)return 0; //空树的高度是0
  58. return node.height;
  59. }
  60. //计算平衡因子 : 左子树高度-右子树高度
  61. private int getBalanceFactor(Node node){
  62. if(node == null)return 0;
  63. return getHeight(node.left) - getHeight(node.right);
  64. }
  65. //判断一棵树是不是二叉搜索树
  66. private boolean isBST(){
  67. ArrayList<K>keys = new ArrayList<>();
  68. inOrder(root,keys);
  69. for(int i = 1; i < keys.size(); i++){
  70. if(keys.get(i-1).compareTo(keys.get(i)) > 0)return false;
  71. }
  72. return true;
  73. }
  74. private void inOrder(Node node, ArrayList<K> keys) {
  75. if(node == null )return;
  76. inOrder(node.left,keys);
  77. keys.add(node.key);
  78. inOrder(node.right,keys);
  79. }
  80. //判断这颗二叉树是不是平衡二叉树
  81. private boolean isBalanced(){
  82. return isBalanced(root);
  83. }
  84. private boolean isBalanced(Node node) { // self is balance and child is balance
  85. if(node == null)return true; // empty tree is a balance tree
  86. if(Math.abs(getBalanceFactor(node)) > 1)return false;
  87. return isBalanced(node.left) && isBalanced(node.right);
  88. }
  89. /** 对节点y进行向右旋转操作,返回旋转后新的根节点x
  90. 右旋转 y x
  91. / \ / \
  92. x T4 向右旋转 (y) z y
  93. / \ - - - - - - - -> / \ / \
  94. z T3 T1 T2 T3 T4
  95. / \
  96. T1 T2
  97. */
  98. private Node rightRotate(Node y){ // y是失衡点
  99. Node x = y.left;
  100. Node T3 = x.right;
  101. x.right = y;
  102. y.left = T3;
  103. //调整之后需要更新height 注意要先更新y的height
  104. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  105. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  106. return x;
  107. }
  108. /** 对节点y进行向左旋转操作,返回旋转后新的根节点x
  109. y x
  110. / \ / \
  111. T4 x 向左旋转 (y) y z
  112. / \ - - - - - - - -> / \ / \
  113. T3 z T4 T3 T1 T2
  114. / \
  115. T1 T2
  116. */
  117. private Node leftRotate(Node y){
  118. Node x = y.right;
  119. Node T3 = x.left;
  120. x.left = y;
  121. y.right = T3;
  122. //更新height
  123. y.height = 1 + Math.max(getHeight(y.left),getHeight(y.right));
  124. x.height = 1 + Math.max(getHeight(x.left),getHeight(x.right));
  125. return x;
  126. }
  127. //向AVL树中添加新的元素(key,valu)
  128. public void add(K key,V value){
  129. root = add(root,key,value);
  130. }
  131. private Node add(Node node,K key,V value){
  132. if(node == null){
  133. size++;
  134. return new Node(key, value); //新建默认高度是height = 1
  135. }
  136. if(key.compareTo(node.key) < 0){
  137. node.left = add(node.left,key,value);
  138. }else if(key.compareTo(node.key) > 0){
  139. node.right = add(node.right,key,value);
  140. }else { // update
  141. node.value = value;
  142. }
  143. /** 1) 更新height */
  144. node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
  145. /** 2)计算平衡因子 */
  146. int balanceFactor = getBalanceFactor(node);
  147. /** 3)调整 : 分为四种情况下的调整*/
  148. /** LL型 */
  149. if(balanceFactor > 1 && getBalanceFactor(node.left) >= 0)
  150. return rightRotate(node);
  151. /** RR型 */
  152. if(balanceFactor < -1 && getBalanceFactor(node.right) <= 0){
  153. return leftRotate(node);
  154. }
  155. /** LR型 */
  156. if(balanceFactor > 1 && getBalanceFactor(node.left) < 0){
  157. node.left = leftRotate(node.left); //左孩子先进行左旋转
  158. return rightRotate(node); //自己再进行右旋转
  159. }
  160. /** RL型 */
  161. if(balanceFactor < -1 && getBalanceFactor(node.right) > 0){
  162. node.right = rightRotate(node.right); //右孩子先进行右旋转
  163. return leftRotate(node); //自己再进行左旋转
  164. }
  165. return node;
  166. }
  167. //返回以node为根节点的二分搜索树中,key所在的结点
  168. public Node getNode(Node node,K key){
  169. if(node == null)
  170. return null;
  171. if(key.compareTo(node.key) == 0){
  172. return node;
  173. }else if(key.compareTo(node.key) < 0) {
  174. return getNode(node.left,key);
  175. }else {
  176. return getNode(node.right,key);
  177. }
  178. }
  179. public boolean contains(K key){
  180. return getNode(root,key) != null;
  181. }
  182. public V get(K key){
  183. Node node = getNode(root,key);
  184. return node == null ? null : node.value;
  185. }
  186. public void set(K key,V newValue){
  187. Node node = getNode(root,key);
  188. if(node == null)
  189. throw new IllegalArgumentException(key + " doesn't exist !");
  190. node.value = newValue;
  191. }
  192. // 返回以node 为根的二分搜索树 最小值所在的结点
  193. private Node minumum(Node node){
  194. if(node.left == null)
  195. return node;
  196. return minumum(node.left);
  197. }
  198. //移除以node为根的二叉搜索树中最小值的结点,返回删除结点之后新的二叉搜索树的根
  199. private Node removeMin(Node node){
  200. if(node.left == null){
  201. Node rightNode = node.right;
  202. node.right = null;
  203. size--;
  204. return rightNode;
  205. }
  206. node.left = removeMin(node.left); //递归 and connect 例如下面返回一个rightNode, 我的left就连接上它
  207. return node;
  208. }
  209. public V remove(K key){
  210. Node node = getNode(root,key);
  211. if(node != null){
  212. root = remove(root,key);
  213. return node.value;
  214. }
  215. return null;
  216. }
  217. private Node remove(Node node, K key){
  218. if(node == null)return null;
  219. Node retNode = null;
  220. if(key.compareTo(node.key) < 0){
  221. node.left = remove(node.left,key);
  222. retNode = node;
  223. }else if(key.compareTo(node.key) > 0){
  224. node.right = remove(node.right,key);
  225. retNode = node;
  226. }else {
  227. //和BST不同 三种情况是一个互斥的关系
  228. if(node.left == null){
  229. Node rightNode = node.right;
  230. node.right = null;
  231. size--;
  232. retNode = rightNode;
  233. }else if(node.right == null){
  234. Node leftNode = node.left;
  235. node.left = null;
  236. size--;
  237. retNode = leftNode;
  238. }else {
  239. Node successor = minumum(node.right);
  240. // successor.right = removeMin(node.right); //这里和BST 不同,这里有可能会破坏平衡,所以不用这个
  241. successor.right = remove(node.right,successor.key); /**这里自己可以维护平衡*/
  242. successor.left = node.left;
  243. node.right = node.left = null;
  244. retNode = successor;
  245. }
  246. }
  247. /**特判 和 BST不同*/
  248. if(retNode == null)return null;
  249. /** 1) 更新height */
  250. retNode.height = 1 + Math.max(getHeight(retNode.left), getHeight(retNode.right));
  251. /** 2)计算平衡因子 */
  252. int balanceFactor = getBalanceFactor(retNode);
  253. /** 3)调整 : 分为四种情况下的调整*/
  254. /** LL型 */
  255. if(balanceFactor > 1 && getBalanceFactor(retNode.left) >= 0)
  256. return rightRotate(retNode);
  257. /** RR型 */
  258. if(balanceFactor < -1 && getBalanceFactor(retNode.right) <= 0){
  259. return leftRotate(retNode);
  260. }
  261. /** LR型 */
  262. if(balanceFactor > 1 && getBalanceFactor(retNode.left) < 0){
  263. retNode.left = leftRotate(retNode.left); //左孩子先进行左旋转
  264. return rightRotate(retNode); //自己再进行右旋转
  265. }
  266. /** RL型 */
  267. if(balanceFactor < -1 && getBalanceFactor(retNode.right) > 0){
  268. retNode.right = rightRotate(retNode.right); //右孩子先进行右旋转
  269. return leftRotate(retNode); //自己再进行左旋转
  270. }
  271. return retNode;
  272. }
  273. }
  274. }