【算法】二叉树单旋

159 阅读2分钟

对二叉树进行单旋的目的,是把一棵不平衡的二叉树变成平衡的二叉树
二叉树的单旋包括 左单旋右单旋

某一节点不平衡 如果左边浅,右边深,进行左单旋

举个例子

现在我们要给它进行单旋,使它变成一棵平衡的二叉树

  • 先明确几个概念

    • 旋转节点:不平衡的节点为旋转节点(A)
    • 新根:旋转之后成为根节点的节点(C)
    • 变化分支:父级节点发生变化的那个分支
    • 不变分支:父级节点不变的那个分支
  • 左单旋时

    • 旋转节点:当前不平衡的节点
    • 新根:右子树的根节点
    • 变化分支:旋转节点右子树的左子树
    • 不变分支:旋转节点右子树的右子树

左单旋可以分为以下几个步骤

  1. 找到新根(C)

  2. 找到变化分支(F)

  3. 当前旋转节点(A)的右孩子为变化分支(F)

  1. 新根的左孩子为旋转节点

  1. 返回根节点(C)

某一节点不平衡
如果右边浅,左边深,进行右单旋

  • 右单旋时

    • 旋转节点:当前不平衡的节点
    • 新根:左子树的根节点
    • 变化分支:旋转节点的左子树的右子树
    • 不变分支:旋转节点左子树的左子树

右单旋可以分为以下几个步骤

  1. 找到新根
  2. 找到变化分支
  3. 当前旋转节点的左孩子为变化分支
  4. 新根的右孩子为旋转节点
  5. 返回新的根节点
function Node(value) {
    this.value = value;
    this.left = null;
    this.right = null;
}

let nodeA = new Node("A");
let nodeC = new Node("C");
let nodeF = new Node("F");
let nodeG = new Node("G");

nodeA.right = nodeC;
nodeC.left = nodeF;
nodeC.right = nodeG;

function getDeep(root) {
    if (!root) return 0;
    let leftDeep = getDeep(root.left);
    let rightDeep = getDeep(root.right);
    return Math.max(leftDeep, rightDeep) + 1;
}

function isBalance(root) {
    if (!root) return true;
    let leftDeep = getDeep(root.left);
    let rightDeep = getDeep(root.right);
    if (Math.abs(leftDeep - rightDeep) > 1) {//不平衡
        return false;
    } else {
        return isBalance(root.left) && isBalance(root.right);
    };
}

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);

    let leftDeep = getDeep(root.left);
    let rightDeep = getDeep(root.right);

    if (Math.abs(leftDeep - rightDeep) < 2) {
        return root;
    } else if (leftDeep > rightDeep) {// 左边深,要右旋
        return rightRoate(root);
    } else {// 右边深要左旋
        return leftRoate(root);
    };
}

function leftRoate(root) {
    //1. 找到新的根
    let newRoot = root.right;
    //2 找到变化分支
    let changeTree = root.right.left;
    //3. 当前旋转节点的右孩子为变化分支
    root.right = changeTree;
    //4. 新根的左孩子为旋转节点
    newRoot.left = root;
    return newRoot;
}

function rightRoate(root) {
    //1. 找到新根
    let newRoot = root.left;
    //2. 找到变化分支
    let changeTree = root.left.right;
    //3. 当前旋转节点的左孩子为变化分支
    root.left = changeTree;
    //4. 新根的有孩子为旋转节点
    root.right = root;
    return newRoot;
}
console.log(isBalance(nodeA));
let newRoot = change(nodeA);
console.log(isBalance(newRoot));