Skip to content

红黑树

RedBlackTree

  • tree/red-black-tree/RedBlackTree.js
import BinarySearchTree from '../binary-search-tree/BinarySearchTree.js';

const RED_BLACK_TREE_COLORS = {
  red: 'red',
  black: 'black',
};

const COLOR_PROP_NAME = 'color';

/**
 * 红黑树是一种含有红黑结点并能自平衡的二叉查找树。
 * ![x](http://river.chenleileicode.com/algorithms/red-black-tree.svg)
 * 
 * 性质:
 * 1:每个节点要么是黑色,要么是红色。
 * 2:根节点是黑色。
 * 3:叶子节点是NIL且黑色。
 * 4:红色结点的两个孩子都是黑色(不存在两个相邻的红色结点)。
 * 5:任意一结点到每个叶子结点的路径,都经过数量相同的黑结点。
 * 所以,我们叫红黑树这种平衡为 **黑色完美平衡**。
 * 
 * 操作:
 * 红黑树能自平衡,它靠的是什么?三种操作:左旋、右旋和变色。
 * 红黑树总是通过旋转和变色达到自平衡。
 * */
export default class RedBlackTree extends BinarySearchTree {
  /**
   * @param {*} value
   * @return {BinarySearchTreeNode}
   */
  insert(value) {
    const insertedNode = super.insert(value);

    if (this.nodeValueComparator.equal(this.root, insertedNode)) {
      this.makeNodeBlack(insertedNode);
    } else {
      this.makeNodeRed(insertedNode);
    }

    this.balance(insertedNode);

    return insertedNode;
  }

  /**
   * @param {*} value
   * @return {boolean}
   */
  remove(value) {}

  /**
   * @param {BinarySearchTreeNode} node
   * 1. 插入空树,不调整;
   * 2. 插入结点的父结点为黑色,不调整;
   * 3. 叔叔存在且为红色,变色;
   * 4. 如果叔叔不存在或叔叔为黑色,旋转。
   */
  balance(node) {
    // 1. 插入空树,不调整
    if (this.nodeValueComparator.equal(node, this.root)) {
      return;
    }

    // 2. 插入结点的父结点为黑色,不调整。
    if (this.isNodeBlack(node.parent)) {
      return;
    }

    const grandParent = node.parent.parent;     

    if (node.uncle && this.isNodeRed(node.uncle)) {
      // 3. 叔叔存在且为红色,变色;
      this.makeNodeBlack(node.uncle);
      this.makeNodeBlack(node.parent);

      if (this.nodeValueComparator.equal(grandParent, this.root)) {
        // 如果祖父是根节点,啥也不干
        return;
      }

      this.makeNodeRed(grandParent);
      this.balance(grandParent); // 递归
    } else if (!node.uncle || this.isNodeBlack(node.uncle)) {
      // 4. 如果叔叔不存在或叔叔为黑色,旋转。
      if (!grandParent) {
        // 如果没有祖父,啥也不干
        return; 
      }

      let newGrandParent; // 旋转之后的新祖父
      if (this.nodeValueComparator.equal(grandParent.left, node.parent)) {
        // 左旋
        if (this.nodeValueComparator.equal(node.parent.left, node)) {
          // 左左旋
          newGrandParent = this.leftLeftRotation(grandParent)
        } else {
          // 左右旋
          newGrandParent = this.leftRightRotation(grandParent)
        }
      } else {
        // 右旋
        if (this.nodeValueComparator.equal(node.parent.right, node)) {
          // 左左旋
          newGrandParent = this.rightRightRotation(grandParent)
        } else {
          // 左右旋
          newGrandParent = this.rightLeftRotation(grandParent)
        }
      }

      this.balance(newGrandParent); // 递归
    }
  }

  /**
   * Left Left Case
   * @param {BinarySearchTreeNode|BinaryTreeNode} grandParentNode
   * @return {BinarySearchTreeNode}
   */
  leftLeftRotation(grandParentNode) {
    // detach parentNode from grandParentNode
    const parentNode = grandParentNode.left;
    grandParentNode.setLeft(null);

    if (grandParentNode.parent) {
      grandParentNode.parent.replaceChild(grandParentNode, parentNode);
    } else {
      this.root = parentNode;
      parentNode.parent = null;
    }

    // Move parent's right subtree to grandParentNode's left subtree.
    if (parentNode.right) {
      grandParentNode.setLeft(parentNode.right);
    }

    parentNode.setRight(grandParentNode);

    this.makeNodeRed(grandParentNode);
    this.makeNodeBlack(parentNode);

    return parentNode;
  }

  leftRightRotation(grandParentNode) {
    // detach parentNode from grandParentNode
    const parentNode = grandParentNode.left;
    grandParentNode.setLeft(null);
    // detach node from parentNode
    const childNode = parentNode.right;
    parentNode.setRight(null);

    // Move child's left subtree to parent's right subtree.
    if (childNode.left) {
      parentNode.setRight(childNode.left);
    }

    childNode.setLeft(parentNode);
    grandParentNode.setLeft(childNode);

    return this.leftLeftRotation(grandParentNode);
  }

  /**
   * Right Right Case
   * @param {BinarySearchTreeNode|BinaryTreeNode} grandParentNode
   * @return {BinarySearchTreeNode}
   */
  rightRightRotation(grandParentNode) {
    // detach parentNode from grandParentNode
    const parentNode = grandParentNode.right;
    grandParentNode.setRight(null);

    if (grandParentNode.parent) {
      grandParentNode.parent.replaceChild(grandParentNode, parentNode);
    } else {
      this.root = parentNode;
      parentNode.parent = null;
    }

    // Move parent's right subtree to grandParentNode's left subtree.
    if (parentNode.left) {
      grandParentNode.setRight(parentNode.left);
    }

    parentNode.setLeft(grandParentNode);

    this.makeNodeRed(grandParentNode);
    this.makeNodeBlack(parentNode);

    return parentNode;
  }

  rightLeftRotation(grandParentNode) {
    // detach parentNode from grandParentNode
    const parentNode = grandParentNode.right;
    grandParentNode.setRight(null);
    // detach left child from parent
    const childNode = parentNode.left;
    parentNode.setLeft(null);

    // 移动孩子的右子树为父亲的左子树
    if (childNode.right) {
      parentNode.setLeft(childNode.right);
    }

    childNode.setRight(parentNode);
    grandParentNode.setRight(childNode);

    return this.rightRightRotation(grandParentNode);
  }

  makeNodeRed(node) {
    node.meta.set(COLOR_PROP_NAME, RED_BLACK_TREE_COLORS.red);

    return node;
  }

  makeNodeBlack(node) {
    node.meta.set(COLOR_PROP_NAME, RED_BLACK_TREE_COLORS.black);

    return node;
  }

  isNodeRed(node) {
    return node.meta.get(COLOR_PROP_NAME) === RED_BLACK_TREE_COLORS.red;
  }

  isNodeBlack(node) {
    return node.meta.get(COLOR_PROP_NAME) === RED_BLACK_TREE_COLORS.black;
  }
}