如下图

单旋的弊端详细版.gif

你会发现使用右单旋与左单旋无法让其恢复为二叉平衡树
那是因为 节点6节点7 这两个节点是变化分支。且是唯一的最深分支

左右双旋

当要对某个节点进行右单旋时
如果变化分支是唯一的最深分支,那么我们要对新根进行左单旋
然后再进行右单旋
这样的旋转叫做左右双旋

示例

图示

image.png

左右双旋过程动态图

双旋.gif

最终图示

image.png

右左双旋

当要对某个节点进行左单旋时
如果变化分支是唯一的最深分支,那么我们要对新根进行右单旋
然后再进行左单旋
这样的旋转叫做右左双旋

左右双旋与右左双旋代码

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