触发条件

如果变化分支的高度比旋转节点的另一侧高度差距超过2,那吗单旋之后依旧不平衡,需要左左双旋或右右双旋

解析

image.png
观察上图可得
变化分支的深度为2
旋转节点的另一侧指的就是,根节点9 的右子树,因为变化分支在根节点9的左子树上故另一侧为根节点9的右子树
变化分支的深度 比 旋转节点的深度 差距大于2 符合右右双旋

图例 :右右双旋

image.png
image.png这个二叉树不是平衡二叉树,因为根节点只有左子树,且左子树的深度为三,为此进行右单旋
右单旋后的图示:
image.png
image.png右单旋后你会发现新根节点6 的右子树9不是二叉平衡树因为右子树9的左子树深度为2 右子树深度为0,故无法构成平衡二叉树
将新根节点6的右子树9进行右单旋后到图示:
image.png
image.png
为平衡二叉树
右右单旋的动态示意图
右右双旋.gif

右右双旋

如果变化分支的高度比旋转节点的另一侧高度差距超过2
先进行一次右单旋后,当右单旋后将右子树进行右单旋,之后将整个二叉树判断为是否为平衡二叉树

左左双旋

与右右单旋差不多
如果变化分支的高度比旋转节点的另一侧高度差距超过2
先进行一次左单旋后,当右单旋后将左子树进行左单旋,之后将整个二叉树判断为是否为平衡二叉树

实现代码

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.left = null;
  5. this.right = null;
  6. }
  7. }
  8. const node9 = new Node('9');
  9. const node8 = new Node('8');
  10. const node7 = new Node('7');
  11. const node6 = new Node('6');
  12. const node5 = new Node('5');
  13. const node4 = new Node('4');
  14. node9.left = node6;
  15. node6.left = node5;
  16. node5.left = node4;
  17. node6.right = node7;
  18. node7.right = node8;
  19. /**
  20. * 利用单旋将二叉不平衡树变为二叉平衡树
  21. * @param {*} root 跟节点
  22. * @returns 返回平衡之后的根节点
  23. */
  24. function change(root) {
  25. if (isBalanceTree(root)) return root; // 返回平衡之后的根节点
  26. // 走到这说明不是二叉平衡树
  27. // 判断平不平衡需要从下往上判断
  28. // 判断对二叉树进行平衡操作要按照后序遍历的顺序
  29. // 后序遍历 先左子树 后右子树 最后自己
  30. // 为何进行后序遍历,当根节点不是二叉平衡树,可能对子树进行单旋操作就成为二叉平衡树
  31. if (root.left != null) root.left = change(root.left); // change函数会返回平衡之后的跟节点,故需要接收返回的的根节点
  32. if (root.right != null) root.right = change(root.right);// change函数会返回平衡之后的跟节点,故需要接收返回的的根节点
  33. // 获取深度
  34. let leftTree = getDeep(root.left);
  35. let rightTree = getDeep(root.right);
  36. // 判断那里不符合二叉平衡树的要求
  37. if (Math.abs(leftTree - rightTree) < 2) return root;
  38. if (leftTree < rightTree) { //左浅 右深 左单旋
  39. // 获取变化分支的深度
  40. let changeTree = getDeep(root.right.left)
  41. // 获取不变分支的深度
  42. let noChangeTree = getDeep(root.right.right)
  43. // 当变化分支的深度大于不变化的分支时需要右左双旋
  44. if (changeTree > noChangeTree) {
  45. //将右子树进行右单旋
  46. root.right = rightRotate(root.right)
  47. }
  48. // 生成左单旋后的二叉树
  49. let newRoot = leftRotate(root)
  50. // 将其生成后的新二叉树的左子树进行判断
  51. newRoot.left = change(newRoot.left)
  52. // 新二叉树的左子树会发生改变,因此将再次判断整个二叉树
  53. newRoot = change(newRoot)
  54. return newRoot;
  55. } else if (leftTree > rightTree) { //左深 右浅 右单旋
  56. // 获取变化分支的深度
  57. let changeTree = getDeep(root.left.right)
  58. // 获取不变分支的深度
  59. let noChangeTree = getDeep(root.left.left)
  60. // 当变化分支的深度大于不变化分支的深度将进行左右单旋
  61. if (changeTree > noChangeTree) {
  62. // 将其左子树进行左单旋
  63. root.left = leftRotate(root.left)
  64. }
  65. let newRoot = rightRotate(root);
  66. // 将其生成后的新二叉树的右子树进行判断
  67. newRoot.right = change(newRoot.right)
  68. // 新二叉树的右子树会发生改变,因此将再次判断整个二叉树
  69. newRoot = change(newRoot)
  70. return newRoot
  71. }
  72. return root
  73. }
  74. /**
  75. * 判断是否为二叉平衡树
  76. * 平衡二叉的满足条件
  77. * 根节点的左子树与右子树的高度差不能超过1
  78. * 这棵二叉树的每个子树的符合第一条
  79. * @param {*} root 根节点
  80. * @returns Boolean
  81. */
  82. function isBalanceTree(root) {
  83. if (!root) return true;
  84. // 获取左右子树的深度
  85. let left = getDeep(root.left);
  86. let right = getDeep(root.right);
  87. // 当左子树与右子树的深度差大于1说明不是二叉平衡树
  88. if (Math.abs(left - right) > 1) return false;
  89. // 遍历这个二叉树的每一个子树
  90. else return isBalanceTree(root.left) && isBalanceTree(root.right);
  91. }
  92. /**
  93. * 获取二叉树的深度
  94. * @param {*} root 节点
  95. * @returns Number 返回左右子树中最深的层数
  96. */
  97. function getDeep(root) {
  98. if (!root) return 0;
  99. let left = getDeep(root.left);
  100. let right = getDeep(root.right);
  101. return Math.max(left, right) + 1;
  102. }
  103. /**
  104. * 左单旋
  105. * 旋转节点:不平衡的节点为旋转节点
  106. * 新根:旋转节点的右子树的根节点
  107. * @param {*} root 旋转节点
  108. * @returns 新的根节点
  109. */
  110. function leftRotate(root) {
  111. // 找到新根
  112. let newNode = root.right;
  113. // 找到变化分支
  114. let changeNode = newNode.left;
  115. // 当前旋转节点的右孩子为变化分支
  116. root.right = changeNode;
  117. // 新根的左孩子为旋转节点
  118. newNode.left = root;
  119. // 返回新的根节点
  120. return newNode;
  121. }
  122. /**
  123. * 右单旋
  124. * 旋转节点:不平衡的节点为旋转节点
  125. * 新根:旋转节点的左子树的根节点
  126. * @param {*} root 旋转节点
  127. * @returns 新的根节点
  128. */
  129. function rightRotate(root) {
  130. // 找到新根
  131. let newNode = root.left;
  132. // 找到变化分支
  133. let changeNode = newNode.right;
  134. // 当前旋转节点的左孩子为变化分支
  135. root.left = changeNode
  136. // 新根的右孩子为旋转节点
  137. newNode.right = root;
  138. // 返回新的根节点
  139. return newNode;
  140. }
  141. /**
  142. * 判断是否为平衡二叉树
  143. * 平衡二叉的满足条件
  144. * 根节点的左子树与右子树的高度差不能超过1
  145. * 这棵二叉树的每个子树的符合第一条
  146. * @param {*} root 节点
  147. * @returns Boolean
  148. */
  149. function isBalanceTree(root) {
  150. if (!root) return true;
  151. let letTree = getDeep(root.left)
  152. let rightTree = getDeep(root.right)
  153. if (Math.abs(letTree - rightTree) > 1) return false
  154. else return isBalanceTree(root.left) && isBalanceTree(root.right);
  155. }
  156. // 测试右右双旋
  157. let root = change(node9);
  158. console.log(isBalanceTree(root))