旋转AVL(平衡)二叉树

119 阅读3分钟

一个二叉树的层数越少,那么他查询的速度就会更快。所以我们要尽可能的去减少二叉树层数,同时还要保证所有的元素都在这棵树上。所以我们才需要去做二叉搜索树的平衡。我们的目的也正是减少二叉树的层数

完全二叉树,满二叉树这些都是AVL树。他们满足AVL树的所有条件

对于AVL树不是很了解的可以先看下我二叉平衡树文章

左旋

例1:

    2
     \
      4
     /  \
    3    5

根节点为2,左深度为0,右深度为2。左右深度相差大于1。要向左平衡,即左旋。

右旋

例2:

     2
   /
  4
 /  \
3    5

根节点为2,左深度为2,右深度为0。左右深度相差大于1。要向右平衡,即右旋。


通过上面的两种旋转,并不能100%的构建出AVL树。因为还有其他情况。比如:

例3:

   2
  / \
 1   5
    / \
   4   6
  /
 3

首先对于节点2不平衡,需要向左旋转。左旋之后:

例4:

       5
      / \
     2   6
    /  \  
   1    4
       /
      3

仍然不平衡,这种特殊的情况,还需做以下的操作。

左右双旋

例3中,有以下几个特点:

  1. 二叉树左右深度相差超过2
  2. 节点4是原来5唯一最深的子节点。左旋之后需要更改节点4。由节点5的left改为节点2的right。

当满足以上条件时需要先对节点5进行一次右旋

例5:

   2
  / \
 1   4
    / \
   3   5
        \
         6
         

然后再进行左旋

例6:

       4
      / \
     2   5
    / \   \
   1   3   6

这时才是一棵平衡树

右左双旋

例7:

       9
      / \
     5   10
    / \
   4   6
        \
         7

例7中,有以下几个特点:

  1. 二叉树左右深度相差超过2
  2. 节点6是原来5唯一最深的子节点。左旋之后需要更改节点6。由节点5的right9的left。

当满足以上条件时需要先对节点5进行一次左旋

       9
      / \
     6   10
    / \
   5   7
  /     
 4   

然后再进行右旋

       6
      / \
     5   9
    /   /  \
   4   7   10
   

这时才是一棵平衡树


通过上面的左旋左右旋右旋右左旋之后树的结构发生了变化,很可能导致旋转后仍然不平衡。所以需要再进行一次同向旋转。

右右双旋

例8:

        5
       / \
      2   6
     / \
    1   3
   /     \
  0       4
  

右旋后

       2
      / \
     1   5
        / \
       3   6
      / \
     0   4
     

此时仍然不满足AVL树。需要再对根节点2right进行一次旋转(不一定是左旋或者右旋,根据左右的子节点的深度进行判断)

左左双选

右右双旋同理,在左旋后需要对左子树再进行一次旋转(不一定是左旋或者右旋,根据左右的子节点的深度进行判断)


我们最好再进行一次平衡判断来确保万无一失

代码实现

/**
 * 构造节点
 */
function Node(value) {
  this.value = value;
  this.left = null;
  this.right = null;
}
/**
 * 获取节点最大深度
 */
function getDeep(root) {
  if (root === null) {
    return 0;
  }
  const left = getDeep(root.left);
  const right = getDeep(root.right);
  return Math.max(left, right) + 1;
}
/**
 * 判断是节点否为平衡二叉树
 */
function isBalance(root) {
  if(root === null) {
    return true;
  }
  const leftDeep = getDeep(root.left);
  const rightDeep  = getDeep(root.right);
  if(Math.abs(leftDeep - rightDeep) > 1) {
    return false;
  }
  return isBalance(root.left) && isBalance(root.right);
}
/**
 * 左旋
 */
function rotateLeft(root) {
  const temp = root.right;
  temp.left = root;
  root.right = temp.left;
  return temp;
}
/**
 * 右旋
 */
function rotateRight(root) {
  const temp = root.left;
  temp.right = root;
  root.left = temp.right;
  return temp;
}

/**
 * 调整二叉树为AVL
 */
function change(root){
  if(isBalance(root)) {
    return root;
  }

  if(root.right !== null) {
    // 对右子树进行递归调整
    root.right = change(root.right)
  }
  if(root.left !== null) {
    // 对左子树进行递归调整
    root.left = change(root.left)
  }
  const leftDeep = getDeep(root.left)
  const rightDeep = getDeep(root.right)

  if(leftDeep > rightDeep) {
    // 右旋时需要被改变的节点(root.left.right)
    const changeTreeDeep = getDeep(root.left.right)
    // 右旋时不需要被改变的节点(root.left.left)
    const unChangeTreeDeep = getDeep(root.left.left)
    
    if(changeTreeDeep > unChangeTreeDeep) {
        // 右左旋转
        root.left = rotateLeft(root.left);
    }
    // 右旋
    const newRoot = rotateRight(root);
    // 右右旋转
    newRoot.right = change(newRoot.right);
    // 再次平衡确认
    return change(newRoot)
  } else {
    // 左旋时需要被改变的节点(root.right.left)
    const changeTreeDeep = getDeep(root.right.left)
    // 右旋时不需要被改变的节点(root.right.right)
    const unChangeTreeDeep = getDeep(root.left.left)
    if(changeTreeDeep > unChangeTreeDeep) {
       // 左右旋转
       root.right = rotateLeft(root.right);
    }
    // 左旋
    const newRoot = rotateLeft(root);
    // 左左旋转
    newRoot.left = change(newRoot.left);
    // 再次平衡确认
    return change(newRoot)
  }
}

/**
 *        2
 *      /   \
 *     1    4
 *        /   \
 *       3     5
 */
const a1 = new Node(2)
const b1 = new Node(3)
const c1 = new Node(4)
const d1 = new Node(5)
const e1 = new Node(1)

a1.left = e1;
a1.right = c1;

c1.left = b1;
c1.right = d1;


console.log(isBalance(change(a1)));
// true