红黑树(RedBlackTree)
自平衡二叉搜索树之一,与 4 阶 B 树具有等价性
必须满足的 5 个性质
- 节点只能是 Red 或 Black
- 根节点是 Black
- 叶子节点(遐想节点)是 Black
- Red 节点的子节点都是 Black
- Red 节点的父节点都是 Black
- 根节点到叶子节点的所有路径上不能有两个连续的 Red 节点
- 从任一节点到叶子节点的所有路径都包含相同数目的 Black 节点
核心代码
import { BlanceBinarySearchTree } from './blanceBinarySearchTree';
import { BinaryTreeNode } from './binaryTree';
import { IBinarySearchTree } from './binarySearchTree';
export enum Color {
  RED,
  BLACK,
}
const getColor = (node: RBTreeNode<any> | null) => (node === null ? Color.BLACK : node.color);
const isRed = (node: RBTreeNode<any> | null) => getColor(node) === Color.RED;
const isBlack = (node: RBTreeNode<any> | null) => getColor(node) === Color.BLACK;
export interface IRBTree<T> extends IBinarySearchTree<T> {
  print: () => string;
  levelOrder: () => T[];
}
export class RBTreeNode<T> extends BinaryTreeNode<T> {
  private _color = Color.RED;
  get color() {
    return this._color;
  }
  get sibling() {
    return super.sibling as RBTreeNode<T>;
  }
  /* istanbul ignore next */
  constructor(
    public val: T,
    public parent: RBTreeNode<T> | null = null,
    public left: RBTreeNode<T> | null = null,
    public right: RBTreeNode<T> | null = null
  ) {
    super(val, parent, left, right);
  }
  setRed() {
    return this.setColor(Color.RED);
  }
  setBlack() {
    return this.setColor(Color.BLACK);
  }
  setColor(color: Color) {
    this._color = color;
    return this;
  }
  toString() {
    return super.toString() + (isRed(this) ? '(R)' : '');
  }
}
export class RBTree<T> extends BlanceBinarySearchTree<T> implements IRBTree<T> {
  protected createNode(
    val: T,
    parent: BinaryTreeNode<T> | null = null,
    left: BinaryTreeNode<T> | null = null,
    right: BinaryTreeNode<T> | null = null
  ): BinaryTreeNode<T> {
    return new RBTreeNode(
      val,
      parent as RBTreeNode<T> | null,
      left as RBTreeNode<T> | null,
      right as RBTreeNode<T> | null
    );
  }
  protected afterAdd(node: RBTreeNode<T>) {
    let parent = node.parent;
    // 如果父节点不存在,则添加的是根节点,直接染黑
    if (parent === null) {
      node.setBlack();
      return;
    }
    // 如果父节点是黑节点,则不做处理
    if (isBlack(parent)) return;
    const grandParent = parent.parent!.setRed();
    const uncle = parent.sibling;
    // 如果叔父节点是红色,则进行转换后上溢
    if (isRed(uncle)) {
      parent.setBlack();
      uncle.setBlack();
      this.afterAdd(grandParent);
      return;
    }
    // 判断旋转
    if (parent.isLeftChild) {
      if (node.isRightChild) {
        this.rotateLeft(parent);
        [node, parent] = [parent, node];
      }
      this.rotateRight(grandParent);
    } else {
      if (node.isLeftChild) {
        this.rotateRight(parent);
        [node, parent] = [parent, node];
      }
      this.rotateLeft(grandParent);
    }
    parent.setBlack();
    node.setRed();
  }
  protected afterRemove(node: RBTreeNode<T>) {
    // 如果删除的是红色节点,则染黑后(考虑节点可能是被删除节点的唯一子节点时)不做处理
    if (isRed(node)) {
      node.setBlack();
      return;
    }
    const parent = node.parent;
    // 如果删除的时根节点则不动
    if (parent === null) return;
    // 判断删除的节点是否时左子节点(考虑可能是下溢产生的删除和正常删除)
    const isLeftChild = parent.left === null || node.isLeftChild;
    let sibling = isLeftChild ? parent.right! : parent.left!;
    if (isLeftChild) {
      if (isRed(sibling)) {
        this.rotateLeft(parent);
        sibling.setBlack();
        parent.setRed();
        sibling = parent.right!;
      }
      if (isBlack(sibling.left) && isBlack(sibling.right)) {
        const blackState = isBlack(parent);
        sibling.setRed();
        parent.setBlack();
        blackState && this.afterRemove(parent);
      } else {
        if (isBlack(sibling.right)) {
          this.rotateRight(sibling);
          sibling = sibling.parent!;
        }
        this.rotateLeft(parent);
        sibling.setColor(parent.color);
        parent.setBlack();
        sibling.right?.setBlack();
      }
    } else {
      // 如果叔父节点是红色,则进行旋转使叔父节点染黑
      if (isRed(sibling)) {
        this.rotateRight(parent);
        sibling.setBlack();
        parent.setRed();
        sibling = parent.left!;
      }
      // 如果左右子树都为黑,则无真实子节点,则使父节点下溢
      if (isBlack(sibling.left) && isBlack(sibling.right)) {
        const blackState = isBlack(parent);
        sibling.setRed();
        parent.setBlack();
        blackState && this.afterRemove(parent);
      } else {
        // 如果兄弟节点的左节点是黑色,则进行旋转
        if (isBlack(sibling.left)) {
          this.rotateLeft(sibling);
          sibling = sibling.parent!;
        }
        this.rotateRight(parent);
        sibling.setColor(parent.color);
        sibling.left?.setBlack();
        parent.setBlack();
      }
    }
  }
}
核心代码
/* istanbul ignore file */
import { repeat } from 'lodash';
import { IRBTree } from './rbTree';
const BLACK = false;
const RED = true;
type Color = boolean;
const isRed = (node: RBTreeNode2<any> | null) => node?.isRed ?? false;
const isBlack = (node: RBTreeNode2<any> | null) => node?.isBlack ?? true;
export class RBTreeNode2<T> {
  private _color: Color = RED;
  get color() {
    return this._color;
  }
  get isRed() {
    return this.color === RED;
  }
  get isBlack() {
    return this.color === BLACK;
  }
  get childSize() {
    return this.left === null && this.right === null
      ? 0
      : this.left === null && this.right !== null
      ? 1
      : this.left !== null && this.right === null
      ? 1
      : 2;
  }
  left: RBTreeNode2<T> | null = null;
  right: RBTreeNode2<T> | null = null;
  constructor(public val: T, public parent: RBTreeNode2<T> | null = null) {}
  toRed() {
    return this.toColor(RED);
  }
  toBlack() {
    return this.toColor(BLACK);
  }
  toColor(v: Color) {
    this._color = v;
    return this;
  }
  toString() {
    return `${this.val}【${this.isRed ? 'R' : 'B'}】`;
  }
}
export class RBTree2<T> implements IRBTree<T> {
  private _root: RBTreeNode2<T> | null = null;
  get root() {
    return this._root;
  }
  private _size = 0;
  get size() {
    return this._size;
  }
  get empty() {
    return this.size === 0;
  }
  constructor(private compare: (t1: T, t2: T) => number) {}
  clear() {
    this._root = null;
  }
  contains(v: T): boolean {
    return this.findNode(this.root, v)?.val !== null;
  }
  private findNode(root: RBTreeNode2<T> | null, v: T): RBTreeNode2<T> | null {
    if (root === null) return null;
    if (root.val === v) return root;
    if (this.compare(root.val, v) > 0) return this.findNode(root.left, v);
    else return this.findNode(root.right, v);
  }
  add(v: T): void {
    const oldNode = this.findNode(this.root, v);
    if (oldNode !== null) {
      oldNode.val = v;
      return;
    }
    if (this.root === null) {
      const node = new RBTreeNode2(v);
      this._root = node;
      this._size++;
      this.afterAdd(node);
      return;
    }
    let parent = this.root;
    let pos: 'left' | 'right' = 'left';
    while (parent) {
      const compare = this.compare(parent.val, v);
      if (compare > 0) {
        if (parent.left === null) break;
        parent = parent.left;
      } else {
        if (parent.right === null) {
          pos = 'right';
          break;
        }
        parent = parent.right;
      }
    }
    const node = new RBTreeNode2(v, parent);
    parent[pos] = node;
    this._size++;
    this.afterAdd(node);
  }
  afterAdd(node: RBTreeNode2<T>): void {
    let parent = node.parent;
    if (parent === null) {
      node.toBlack();
      return;
    }
    if (isBlack(parent)) return;
    const grand = parent.parent!;
    const sibling = grand.left === parent ? grand.right! : grand.left!;
    if (isRed(sibling)) {
      node.toRed();
      parent.toBlack();
      sibling.toBlack();
      this.afterAdd(grand.toRed());
      return;
    }
    if (grand.left === parent) {
      if (parent.right === node) {
        this.rotateL(parent);
        [parent, node] = [node, parent];
      }
      this.rotateR(grand);
    } else {
      if (parent.left === node) {
        this.rotateR(parent);
        [parent, node] = [node, parent];
      }
      this.rotateL(grand);
    }
    parent.toBlack();
    node.toRed();
    grand.toRed();
    return;
  }
  remove(v: T): void {
    let node = this.findNode(this.root, v);
    if (node === null) return;
    if (this.size === 1) {
      this._root = null;
      this._size = 0;
      return;
    }
    if (node.childSize === 2) {
      const successor = this.successor(node)!;
      [node.val, successor.val] = [successor.val, node.val];
      node = successor;
    }
    if (node.childSize === 0) {
      const parent = node.parent!;
      const pos = parent.left === node ? 'left' : 'right';
      parent[pos] = null;
      this._size--;
      this.afterRemove(node);
      return;
    }
    const child = node.left ?? node.right!;
    const parent = node.parent!;
    if (parent === null) {
      this._root = child;
      child.parent = null;
      this._size--;
      this.afterRemove(node);
      return;
    }
    const pos = parent.left === node ? 'left' : 'right';
    parent[pos] = child;
    child.parent = parent;
    this.afterRemove(child);
  }
  afterRemove(node: RBTreeNode2<T>): void {
    if (node.isRed) {
      node.toBlack();
      return;
    }
    let parent = node.parent;
    if (parent === null) return;
    const leftChild = parent.left === null || parent.left === node;
    let sibling = leftChild ? parent.right! : parent.left!;
    if (leftChild) {
      if (sibling.isRed) {
        this.rotateL(parent);
        sibling.toBlack();
        parent.toRed();
        sibling = parent.right!;
      }
      if (sibling.childSize === 0) {
        const parentIsBlack = parent.isBlack;
        sibling.toRed();
        parent.toBlack();
        parentIsBlack && this.afterRemove(parent);
      } else {
        if (isBlack(sibling.right)) {
          this.rotateR(sibling);
          sibling = sibling.parent!;
        }
        this.rotateL(parent);
        sibling.toColor(parent.color);
        sibling.right?.toBlack();
        parent.toBlack();
      }
    } else {
      if (sibling.isRed) {
        this.rotateR(parent);
        sibling.toBlack();
        parent.toRed();
        sibling = parent.left!;
      }
      if (isBlack(sibling.left) && isBlack(sibling.right)) {
        const parentIsBlack = parent.isBlack;
        sibling.toRed();
        parent.toBlack();
        parentIsBlack && this.afterRemove(parent);
      } else {
        if (isBlack(sibling.left)) {
          this.rotateL(sibling);
          sibling = sibling.parent!;
        }
        this.rotateR(parent);
        sibling.toColor(parent.color);
        sibling.left?.toBlack();
        parent.toBlack();
      }
    }
  }
  private successor(node: RBTreeNode2<T>): RBTreeNode2<T> | null {
    if (node.right !== null) {
      let successor = node.right;
      while (successor.left) successor = successor.left;
      return successor;
    }
    let successor = node;
    while (successor.parent !== null && successor.parent.right === successor)
      successor = successor.parent;
    return successor;
  }
  private rotateL(grand: RBTreeNode2<T>) {
    const root = grand.parent;
    const parent = grand.right!;
    if (root === null) this._root = parent;
    else {
      const pos = root.left === grand ? 'left' : 'right';
      root[pos] = parent;
    }
    grand.right = parent.left;
    if (parent.left) parent.left.parent = grand;
    parent.left = grand;
    grand.parent = parent;
    parent.parent = root;
  }
  private rotateR(grand: RBTreeNode2<T>) {
    const root = grand.parent;
    const parent = grand.left!;
    if (root === null) this._root = parent;
    else {
      const pos = root.left === grand ? 'left' : 'right';
      root[pos] = parent;
    }
    grand.left = parent.right;
    if (parent.right) parent.right.parent = grand;
    parent.right = grand;
    grand.parent = parent;
    parent.parent = root;
  }
  print(): string {
    return this.root === null ? '' : this._print(this.root);
  }
  private _print(node: RBTreeNode2<T>, prefix = ''): string {
    const left = node.left;
    const right = node.right;
    const nodeStr = node.toString();
    const halfLength = (nodeStr.length + (prefix === '' ? 0 : 3)) >> 1;
    const blankStr = repeat(' ', halfLength);
    const lineStr = repeat('─', halfLength);
    prefix += blankStr;
    let str = nodeStr + `\n`;
    if (right !== null) {
      str +=
        prefix +
        (left === null ? '└' : '├') +
        lineStr +
        ' R ' +
        this._print(right, `${prefix}${left === null ? ' ' : '│'}${blankStr}`);
    }
    if (left !== null) {
      str += `${prefix}└${lineStr}` + ' L ' + this._print(left, `${prefix}${blankStr} `);
    }
    return str;
  }
  levelOrder(): T[] {
    const list: T[] = [];
    if (this.root === null) return list;
    const queue: RBTreeNode2<T>[] = [this.root];
    while (queue.length !== 0) {
      const node = queue.shift()!;
      list.push(node.val);
      node.left && queue.push(node.left);
      node.right && queue.push(node.right);
    }
    return list;
  }
}
核心代码
/* istanbul ignore file */
import { repeat } from 'lodash';
import { IRBTree } from './rbTree';
const RED = true;
const BLACK = false;
type Color = boolean;
const isRed = (node: RBTreeNode3<any> | null) => node?.color === RED;
const isBlack = (node: RBTreeNode3<any> | null) => node === null || node?.color === BLACK;
const setColor = (node: RBTreeNode3<any> | null, color: Color) => {
  if (node) node.color = color;
};
const setRed = (node: RBTreeNode3<any> | null) => setColor(node, RED);
const setBlack = (node: RBTreeNode3<any> | null) => setColor(node, BLACK);
export class RBTreeNode3<T> {
  color = RED;
  left: RBTreeNode3<T> | null = null;
  right: RBTreeNode3<T> | null = null;
  constructor(public val: T, public parent: RBTreeNode3<any> | null = null) {}
  toString() {
    return this.val + (isRed(this) ? '(R)' : '');
  }
}
export class RBTree3<T> implements IRBTree<T> {
  private _size = 0;
  get size() {
    return this._size;
  }
  get empty() {
    return this._size === 0;
  }
  private root: RBTreeNode3<T> | null = null;
  constructor(private compare: (k1: T, k2: T) => number) {}
  clear() {
    this.root = null;
    this._size = 0;
  }
  contains(key: T) {
    return this.findNode(key) !== null;
  }
  add(key: T) {
    let node = this.findNode(key);
    if (this.root === null) {
      this.root = node = new RBTreeNode3<T>(key);
    } else {
      let parent = this.root;
      let pos = 'left';
      while (parent !== null) {
        const compare = this.compare(parent.val, key);
        if (compare > 0) {
          if (parent.left === null) break;
          parent = parent.left;
        } else {
          if (parent.right === null) {
            pos = 'right';
            break;
          }
          parent = parent.right;
        }
      }
      parent[pos] = node = new RBTreeNode3<T>(key, parent);
    }
    this._size++;
    this.afterSet(node);
  }
  private afterSet(node: RBTreeNode3<T>) {
    let parent = node.parent;
    if (parent === null) {
      setBlack(node);
      return;
    }
    if (isBlack(parent)) return;
    const grand = parent.parent!;
    const sibling = grand.left === parent ? grand.right! : grand.left!;
    if (isRed(sibling)) {
      setBlack(parent);
      setBlack(sibling);
      setRed(grand);
      this.afterSet(grand);
      return;
    }
    if (grand.left === parent) {
      if (parent.right === node) {
        this.rotateL(parent);
        [parent, node] = [node, parent];
      }
      this.rotateR(grand);
    } else {
      if (parent.left === node) {
        this.rotateR(parent);
        [parent, node] = [node, parent];
      }
      this.rotateL(grand);
    }
    setBlack(parent);
    setRed(node);
    setRed(grand);
  }
  remove(key: T) {
    let node = this.findNode(key);
    if (node === null) return;
    if (node.left !== null && node.right !== null) {
      const successor = this.successor(node);
      [node.val, successor.val] = [successor.val, node.val];
      node = successor;
    }
    const parent = node.parent!;
    this._size--;
    if (node.left === null && node.right === null) {
      if (this.root === node) {
        this.clear();
        return;
      }
      const pos = parent.left === node ? 'left' : 'right';
      parent[pos] = null;
      this.afterRemove(node);
      return;
    }
    const childNode = node.left ?? node.right!;
    if (parent === null) this.root = childNode;
    else {
      const pos = parent.left === node ? 'left' : 'right';
      parent[pos] = childNode;
    }
    childNode.parent = parent;
    this.afterRemove(childNode);
  }
  private afterRemove(node: RBTreeNode3<T>) {
    if (isRed(node)) {
      setBlack(node);
      return;
    }
    const parent = node.parent;
    if (parent === null) return;
    const leftChild = parent.left === null || parent.left === node;
    let sibling = leftChild ? parent.right! : parent.left!;
    if (leftChild) {
      if (isRed(sibling)) {
        this.rotateL(parent);
        setBlack(sibling);
        setRed(parent);
        sibling = parent.right!;
      }
      if (isBlack(sibling.left) && isBlack(sibling.right)) {
        const parentIsBlack = isBlack(parent);
        setRed(sibling);
        setBlack(parent);
        parentIsBlack && this.afterRemove(parent);
        return;
      }
      if (isBlack(sibling.right)) {
        this.rotateR(sibling);
        sibling = sibling.parent!;
      }
      this.rotateL(parent);
      setColor(sibling, parent.color);
      setBlack(parent);
      setBlack(sibling.right);
    } else {
      if (isRed(sibling)) {
        this.rotateR(parent);
        setBlack(sibling);
        setRed(parent);
        sibling = parent.left!;
      }
      if (isBlack(sibling.left) && isBlack(sibling.right)) {
        const parentIsBlack = isBlack(parent);
        setRed(sibling);
        setBlack(parent);
        parentIsBlack && this.afterRemove(parent);
        return;
      }
      if (isBlack(sibling.left)) {
        this.rotateL(sibling);
        sibling = sibling.parent!;
      }
      this.rotateR(parent);
      setColor(sibling, parent.color);
      setBlack(parent);
      setBlack(sibling.left);
    }
  }
  private successor(node: RBTreeNode3<T>) {
    let successor = node.right!;
    while (successor.left) successor = successor.left;
    return successor;
  }
  private findNode(key: T, root = this.root): RBTreeNode3<T> | null {
    if (root === null) return null;
    const compare = this.compare(root.val, key);
    if (compare > 0) return this.findNode(key, root.left);
    if (compare < 0) return this.findNode(key, root.right);
    return root;
  }
  private rotateL(grand: RBTreeNode3<T>) {
    const root = grand.parent;
    const parent = grand.right!;
    if (root === null) this.root = parent;
    else {
      const pos = root.left === grand ? 'left' : 'right';
      root[pos] = parent;
    }
    grand.right = parent.left;
    if (parent.left) parent.left.parent = grand;
    parent.left = grand;
    grand.parent = parent;
    parent.parent = root;
  }
  private rotateR(grand: RBTreeNode3<T>) {
    const root = grand.parent;
    const parent = grand.left!;
    if (root === null) this.root = parent;
    else {
      const pos = root.left === grand ? 'left' : 'right';
      root[pos] = parent;
    }
    grand.left = parent.right;
    if (parent.right) parent.right.parent = grand;
    parent.right = grand;
    grand.parent = parent;
    parent.parent = root;
  }
  print(): string {
    return this.root === null ? '' : this._print(this.root);
  }
  private _print(node: RBTreeNode3<T>, prefix = ''): string {
    const left = node.left;
    const right = node.right;
    const nodeStr = node.toString();
    const halfLength = (nodeStr.length + (prefix === '' ? 0 : 3)) >> 1;
    const blankStr = repeat(' ', halfLength);
    const lineStr = repeat('─', halfLength);
    prefix += blankStr;
    let str = nodeStr + `\n`;
    if (right !== null) {
      str +=
        prefix +
        (left === null ? '└' : '├') +
        lineStr +
        ' R ' +
        this._print(right, `${prefix}${left === null ? ' ' : '│'}${blankStr}`);
    }
    if (left !== null) {
      str += `${prefix}└${lineStr}` + ' L ' + this._print(left, `${prefix}${blankStr} `);
    }
    return str;
  }
  levelOrder(): T[] {
    const list: T[] = [];
    if (this.root === null) return list;
    const queue: RBTreeNode3<T>[] = [this.root];
    while (queue.length !== 0) {
      const node = queue.shift()!;
      list.push(node.val);
      node.left && queue.push(node.left);
      node.right && queue.push(node.right);
    }
    return list;
  }
}