SkyBlog

红黑树

使用 ts 实现的红黑树

这篇文章发布于 2024年02月12日,星期一,07:17,归类于 算法阅读 ? 次,0 条评论

源码,参考

enum Color {
  RED,
  BLACK
}
 
enum Direction {
  ROOT = 0,
  LEFT = -1,
  RIGHT = 1
}
 
class RBTreeNode<T = any> {
  public left: RBTreeNode | null = null
  public right: RBTreeNode | null = null
  public parent: RBTreeNode | null = null
  public color: Color = Color.RED
  public key: number
  public value: T
 
  constructor(key: RBTreeNode['key'], value: T) {
    this.key = key
    this.value = value
  }
 
  public direction() {
    if (this.parent == null) return Direction.ROOT
    return this.parent.left == this ? Direction.LEFT : Direction.RIGHT
  }
 
  public isRoot() {
    return this.direction() == Direction.ROOT
  }
 
  public isLeft() {
    return this.direction() == Direction.LEFT
  }
 
  public isRight() {
    return this.direction() == Direction.RIGHT
  }
 
  public isRed() {
    return this.color == Color.RED
  }
 
  public isBlack() {
    return this.color == Color.BLACK
  }
 
  public sibling() {
    if (this.isRoot()) return null
    return this.isLeft() ? this.parent!.right : this.parent!.left
  }
 
  public uncle() {
    if (this.isRoot()) return null
    return this.parent!.sibling()
  }
 
  public grandParent() {
    if (this.isRoot()) return null
    return this.parent!.parent
  }
}
 
class RBTree<T = never> {
  private count: number
  public root: RBTreeNode<T> | null
 
  constructor() {
    this.count = 0
    this.root = null
  }
 
  private maintainRelationship(node: RBTreeNode) {
    if (node.left != null) {
      node.left.parent = node
    }
    if (node.right != null) {
      node.right.parent = node
    }
  }
 
  //     |                       |
  //     2      l-rotate(2)      4
  //    / \     ==========>     / \
  //   1   4    <==========    2   5
  //      / \   r-rotate(4)   / \
  //     3   5               1   3
 
  // 左旋
  private rotateLeft(node: RBTreeNode | null) {
    if (node == null || node.right == null) return
 
    const direction = node.direction()
    const [parent, successor] = [node.parent, node.right]
 
    node.right = successor.left
    successor.left = node
 
    this.maintainRelationship(node)
    this.maintainRelationship(successor)
 
    switch (direction) {
      case Direction.ROOT:
        this.root = successor
        break
      case Direction.LEFT:
        parent!.left = successor
        break
      case Direction.RIGHT:
        parent!.right = successor
        break
    }
 
    successor.parent = parent
  }
 
  // 右旋
  private rotateRight(node: RBTreeNode | null) {
    if (node == null || node.left == null) return
 
    const direction = node.direction()
    const [parent, successor] = [node.parent, node.left]
 
    node.left = successor.right
    successor.right = node
 
    this.maintainRelationship(node)
    this.maintainRelationship(successor)
 
    switch (direction) {
      case Direction.ROOT:
        this.root = successor
        break
      case Direction.LEFT:
        parent!.left = successor
        break
      case Direction.RIGHT:
        parent!.right = successor
        break
    }
 
    successor.parent = parent
  }
 
  private rotateSameDirection(node: RBTreeNode | null, direction: Direction) {
    direction == Direction.LEFT ? this.rotateLeft(node) : this.rotateRight(node)
  }
 
  private rotateOppositeDirection(node: RBTreeNode | null, direction: Direction) {
    direction == Direction.LEFT ? this.rotateRight(node) : this.rotateLeft(node)
  }
 
  // 清空
  public clear() {
    this.count = 0
    this.root = null
  }
 
  // 添加后
  private maintainAfterAdd(node: RBTreeNode) {
    if (node.isRoot()) return
 
    if (node.parent!.isBlack()) return
 
    if (node.parent!.isRoot()) {
      node.parent!.color = Color.BLACK
      return
    }
 
    if (node.uncle()?.isRed()) {
      node.parent!.color = Color.BLACK
      node.uncle()!.color = Color.BLACK
      node.grandParent()!.color = Color.RED
      this.maintainAfterAdd(node.grandParent()!)
      return
    }
 
    if (node.direction() != node.parent!.direction()) {
      const parent = node.parent!
      node.isLeft() ? this.rotateRight(parent) : this.rotateLeft(parent)
      node = parent
    }
 
    node.parent!.isLeft() ? this.rotateRight(node.grandParent()) : this.rotateLeft(node.grandParent())
 
    node.parent!.color = Color.BLACK
    node.sibling()!.color = Color.RED
  }
 
  // 添加
  public add(key: RBTreeNode['key'], value: T, replace = true) {
    const dfs = (node: RBTreeNode<T> | null) => {
      if (node == null) {
        this.root = new RBTreeNode(key, value)
        return
      }
 
      if (key == node.key) {
        if (replace) node.value = value
        return
      }
 
      if (key < node.key) {
        if (node.left == null) {
          node.left = new RBTreeNode(key, value)
          node.left.parent = node
          this.maintainAfterAdd(node.left)
          this.count++
        } else {
          dfs(node.left)
        }
      } else {
        if (node.right == null) {
          node.right = new RBTreeNode(key, value)
          node.right.parent = node
          this.maintainAfterAdd(node.right)
          this.count++
        } else {
          dfs(node.right)
        }
      }
    }
    dfs(this.root)
  }
 
  // 删除后
  private maintainAfterRemove(node: RBTreeNode) {
    if (node.isRoot()) return
 
    const direction = node.direction()
    let sibling = node.sibling()
 
    if (sibling?.isRed()) {
      const parent = node.parent!
      this.rotateSameDirection(parent, direction)
      sibling.color = Color.BLACK
      parent.color = Color.RED
      sibling = node.sibling()
    }
 
    let closeNephew = direction == Direction.LEFT ? sibling?.left : sibling?.right
    let distantNephew = direction == Direction.LEFT ? sibling?.right : sibling?.left
    const closeNephewIsBlack = closeNephew == null || closeNephew.isBlack()
    const distantNephewIsBlack = distantNephew == null || distantNephew.isBlack()
 
    if (closeNephewIsBlack && distantNephewIsBlack) {
      sibling!.color = Color.RED
      if (node.parent != null && node.parent.isRed()) {
        node.parent.color = Color.BLACK
      } else {
        this.maintainAfterRemove(node.parent!)
      }
    } else {
      if (closeNephew != null && closeNephew.isRed()) {
        this.rotateOppositeDirection(sibling, direction)
        closeNephew.color = Color.BLACK
        sibling!.color = Color.RED
        sibling = node.sibling()
        closeNephew = direction == Direction.LEFT ? sibling?.left : sibling?.right
        distantNephew = direction == Direction.LEFT ? sibling?.right : sibling?.left
      }
      this.rotateSameDirection(node.parent, direction)
      sibling!.color = node.parent!.color
      node.parent!.color = Color.BLACK
      if (distantNephew != null) distantNephew.color = Color.BLACK
    }
  }
 
  // 删除
  public remove(key: RBTreeNode['key']) {
    const dfs = (node: RBTreeNode | null): boolean => {
      if (node == null) return false
 
      if (key != node.key) {
        if (key < node.key) {
          const left = node.left
          if (left != null && dfs(left)) {
            this.maintainRelationship(node)
            return true
          } else {
            return false
          }
        } else {
          const right = node.right
          if (right != null && dfs(right)) {
            this.maintainRelationship(node)
            return true
          } else {
            return false
          }
        }
      }
 
      if (this.count == 1) {
        this.clear()
        return true
      }
 
      if (node.left != null && node.right != null) {
        let [parent, successor] = [node, node.right]
        while (successor.left != null) {
          parent = successor
          successor = successor.left
        }
        node.key = successor.key
        node.value = successor.value
        ;[node, successor] = [successor, node]
        this.maintainRelationship(parent)
      }
 
      if (node.left == null && node.right == null) {
        if (node.isBlack()) this.maintainAfterRemove(node)
        if (node.isLeft()) node.parent!.left = null
        else node.parent!.right = null
      } else {
        let [parent, replacement] = [node.parent, node.left != null ? node.left : node.right]
        switch (node.direction()) {
          case Direction.ROOT:
            this.root = replacement
            break
          case Direction.LEFT:
            parent!.left = replacement
            break
          case Direction.RIGHT:
            parent!.right = replacement
            break
        }
        if (!node.isRoot()) replacement!.parent = parent
        if (node.isBlack()) {
          if (replacement!.isRed()) {
            replacement!.color = Color.BLACK
          } else {
            this.maintainAfterRemove(replacement!)
          }
        }
      }
 
      this.count--
      return true
    }
    return dfs(this.root)
  }
 
  // 数据量
  public size() {
    return this.count
  }
}

测试

const drawTree = <T>(
  root: T | null,
  payload: {
    left: (node: T) => T | null
    right: (node: T) => T | null
    value: (node: T) => string
  }
) => {
  // 中序遍历并记录节点深度
  const nodes: Array<{ depth: number; node: T }> = []
  let maxDepth = 0
  const inorderTraversal = (node: T | null, depth: number) => {
    if (node == null) return null
    inorderTraversal(payload.left(node), depth + 1)
    nodes.push({ depth, node })
    maxDepth = Math.max(maxDepth, depth)
    inorderTraversal(payload.right(node), depth + 1)
  }
  inorderTraversal(root, 0)
 
  // 填入二维矩阵;每一层中间多加了一行空行,用以填写‘树枝’
  const matrix = Array.from<unknown, string[]>({ length: 2 * maxDepth + 1 }, () => Array(nodes.length).fill(''))
  const colsLen = Array.from<number>({ length: nodes.length })
  const nodesMap = new Map<number, Map<number, T>>()
  for (let i = 0; i < nodes.length; i++) {
    const { depth, node } = nodes[i]
    const content = payload.value(node)
    matrix[depth * 2][i] = content
    colsLen[i] = content.length
    nodesMap.set(i, new Map().set(depth * 2, node))
  }
 
  // 添加‘树枝’
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[0].length; j++) {
      if (matrix[i][j] == '') {
        matrix[i][j] = ' '.repeat(colsLen[j])
        continue
      }
      const node = nodesMap.get(j)?.get(i)
      if (!node) continue
      if (payload.left(node) != null) {
        matrix[i + 1][j] = '┘'.padEnd(colsLen[j], ' ')
        for (let k = j - 1; k >= 0; k--) {
          if (matrix[i + 2][k] != '') {
            matrix[i + 1][k] = '┌'.padEnd(colsLen[k], '─')
            break
          }
          matrix[i + 1][k] = ''.padEnd(colsLen[k], '─')
        }
      }
      if (payload.right(node) != null) {
        matrix[i + 1][j] = matrix[i + 1][j] != '' ? '┴'.padEnd(colsLen[j], '─') : '└'.padEnd(colsLen[j], '─')
        for (let k = j + 1; k < matrix[0].length; k++) {
          if (matrix[i + 2][k] != '') {
            matrix[i + 1][k] = '┐'.padEnd(colsLen[k], ' ')
            break
          }
          matrix[i + 1][k] = ''.padEnd(colsLen[k], '─')
        }
      }
    }
  }
 
  console.log(matrix.map(v => v.join('')).join('\n'))
}
 
const rbtree = new RBTree<number>()
Array.from({ length: 50 }, (v, k) => rbtree.add(k, k))
drawTree<RBTreeNode>(rbtree.root, {
  left: node => node.left,
  right: node => node.right,
  value: node => (node.isBlack() ? 'B' : 'R') + String(node.value)
})
const removes = Array.from({ length: 20 }, () => (Math.random() * 20) >> 0)
console.log('\nremove:%s\n', removes.join(', '))
removes.forEach(k => rbtree.remove(k))
drawTree<RBTreeNode>(rbtree.root, {
  left: node => node.left,
  right: node => node.right,
  value: node => (node.isBlack() ? 'B' : 'R') + String(node.value)
})
Snipaste_2024-03-11_17-28-21.png
Snipaste_2024-03-11_17-28-21.png