【JavaScript】二叉平衡树的平衡方法单旋与双旋

不平衡的二叉排序树,可以通过旋转变成平衡二叉树。旋转的方式有左单旋、右单旋、左右双旋、右左双旋、左左双旋和右右双旋。

在进行旋转时,我自定义了一些词语来帮助理解:
旋转节点:不平衡需要旋转的节点
新根:旋转之后成为根节点的节点
变化分支:父节点发生变化的分支
不变分支:父节点没有变化的分支

1.左单旋

如果左子树高度浅,右子树高度深,可以进行左单旋。
旋转节点:当前不平衡的节点(5)
新根:旋转节点的右子节点(7)
变化分支:旋转节点的右节点的左子树(以6为根节点的左子树)
不变分支:旋转节点的右节点的右子树(以9为根节点的右子树)
变换逻辑:
1.找到新根
2.找到变化分支
3.旋转节点的右节点设置变化分支
4.新根的左节点设为旋转节点
5.返回新根节点
在这里插入图片描述

//左单旋
function leftRotate(root) {
  //找到新根
  let newRoot = root.right;
  //找到变化分支
  let changeBranch = root.right.left;
  root.right = changeBranch;
  newRoot.left = root;
  return newRoot;
}

2.右单旋

如果右子树高度浅,左子树高度深,可以进行右单旋。
旋转节点:不平衡的节点(9)
新根:旋转节点的左子节点(6)
变化分支:旋转节点的左节点的右子树(以7为根节点的右子树)
不变分支:旋转节点的左节点的左子树(以5为根节点的左子树)
变换逻辑:
1.找到新根
2.找到变化分支
3.旋转节点的左节点设为变化分支
4.新根的右节点设为旋转节点
5.返回新根节点

在这里插入图片描述

//右单选
function rightRotate(root) {
  //找到新根
  let newRoot = root.left;
  //找到变化分支
  let changeBranch = root.left.right;
  root.left = changeBranch ;
  newRoot.right = root;
  return newRoot;
}

3.左右双旋和右左双旋

左单旋和右单旋无法将所有不平衡的二叉排序树转换为平衡二叉树。当变化分支是唯一的最深分支时,左单旋和右单旋都不能得到平衡二叉树,需要进行双旋。
注意:平衡二叉树的转换中一般使用后序遍历。

4.左右双旋

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

节点9不是平衡节点,左深右浅,需要对它进行右单旋。
当以9为旋转节点进行右单旋时,变化分支根节点为8,是唯一最深分支。需要对新根6先进行左单旋,然后再对节点9进行右单旋,最终达到平衡。

在这里插入图片描述

5.右左双旋

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

//左右双旋&&右左双旋
function changeRotate(root) {
  if (isBlance(root)) {
    return root;
  }
  if (root.left) {
    root.left = changeRotate(root.left);
  }
  if (root.right) {
    root.right = changeRotate(root.right);
  }
  let leftDeep = getDeep(root.left);
  let rightDeep = getDeep(root.right);
  if (Math.abs(leftDeep - rightDeep) < 2) {
    //左子树与右子树深度差不超过1,是平衡的
    return root;
  } else if (leftDeep > rightDeep) {
    //不平衡,左深右浅,需要进行右旋
    //不变分支的深度
    let noChangeBranchDeep = getDeep(root.left.left);
    //变化分支的深度
    let changeBranchDeep = getDeep(root.left.right);
    if (changeBranchDeep > noChangeBranchDeep) {
      //变化分支的深度为最高深度,先对新根左旋
      root.left = leftRotate(root.left);
    }
    return rightRotate(root);
  } else {
    //不平衡,右深左前,需要左旋
    let changeBranchDeep = getDeep(root.right.left);
    let noChangeBranchDeep = getDeep(root.right.right);
    if (changeBranchDeep > noChangeBranchDeep) {
      root.right = rightRotate(root.right);
    }
    return leftRotate(root);
  }
}

let arr2 = [9, 6, 8, 5, 7];
let root2 = createSearchTree(arr2);
//console.log(root2);
console.log(isBlance(root2));
let newRoot2 = changeRotate(root2);
//console.log(newRoot2);
console.log(isBlance(newRoot2));

6.右右双旋和左左双旋

当变化分支不是唯一最深分支时,单旋后可能依旧不平衡,这时需要进行右右双旋或者左左双旋。
在这里插入图片描述


function change(root) {
  //返回平衡之后的根节点
  if (isBalance(root)) {
    return root;
  }
  if (root.left != null) root.left = change(root.left);
  if (root.right != null) root.right = change(root.right);
  var leftDeep = getDeep(root.left);
  var rightDeep = getDeep(root.right);
  if (Math.abs(leftDeep - rightDeep) < 2) {
    return root;
  } else if (leftDeep > rightDeep) {
    //不平衡,左边深,需要右旋
    var changeTreeDeep = getDeep(root.left.right);
    var noChangeTreeDeep = getDeep(root.left.left);
    if (changeTreeDeep > noChangeTreeDeep) {
      root.left = leftRotate(root.left);
    }
    var newRoot = rightRotate(root);
    //右旋后,新根的右子树可能依旧不平衡
    newRoot.right = change(newRoot.right);
    //改变右子树后,可能根节点又不平衡了, 再次对根节点进行判断,
    //如果平衡则直接返回,不平衡则继续进行改变
    newRoot = change(newRoot);
    return newRoot;
  } else {
    //不平衡,右边深,需要左旋
    var changeTreeDeep = getDeep(root.right.left);
    var noChangeTreeDeep = getDeep(root.right.right);
    if (changeTreeDeep > noChangeTreeDeep) {
      root.right = rightRotate(root.right);
    }
    var newRoot = leftRotate(root);
    newRoot.left = change(newRoot.left);
    newRoot = change(newRoot);
    return newRoot;
  }
}

let arr3 = [9, 6, 7, 5, 4, 8];
let root3 = createSearchTree(arr3);
console.log(root3);
let newRoot3 = change(root3);
console.log(newRoot3);
console.log(isBlance(newRoot3));

完结!!


注意:
该文章代码部分使用的方法 isBlance,createSearchTree,具体实现参考我的另一篇文章:【JavaScript】二叉排序树

  • 24
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉排序树是一种特殊的二叉树,它满足以下条件: 1. 对于每个节点,它的子树中的所有节点的值都小于该节点的值; 2. 对于每个节点,它的右子树中的所有节点的值都大于该节点的值; 3. 右子树也都是二叉排序树。 为了实现二叉排序树的查找,我们需要以下步骤: 1. 从根节点开始,比较要查找的值与当前节点的值的大小关系; 2. 如果要查找的值比当前节点的值小,则进入子树继续查找; 3. 如果要查找的值比当前节点的值大,则进入右子树继续查找; 4. 如果要查找的值与当前节点的值相等,则找到目标节点,返回该节点。 下面是使用javascript语言实现二叉排序树查找的代码示例: ```javascript class TreeNode { constructor(val) { this.val = val this.left = null this.right = null } } class BinarySearchTree { constructor() { this.root = null } insert(val) { const node = new TreeNode(val) if (!this.root) { this.root = node } else { let curr = this.root while (true) { if (val < curr.val) { if (!curr.left) { curr.left = node break } else { curr = curr.left } } else if (val > curr.val) { if (!curr.right) { curr.right = node break } else { curr = curr.right } } else { break // 值已存在,不插入 } } } } search(val) { let curr = this.root while (curr) { if (val === curr.val) { return curr } else if (val < curr.val) { curr = curr.left } else { curr = curr.right } } return null } } // 示例 const bst = new BinarySearchTree() bst.insert(5) bst.insert(3) bst.insert(7) bst.insert(2) bst.insert(4) bst.insert(6) bst.insert(8) console.log(bst.search(6)) // TreeNode { val: 6, left: null, right: null } console.log(bst.search(9)) // null ``` 上面的代码中,使用了 `TreeNode` 类表示树节点,使用 `BinarySearchTree` 类表示二叉排序树。在插入节点时,如果插入的值已存在,则不插入。在查找节点时,如果找到则返回目标节点,否则返回 `null`。可以通过示例测试查找的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值