CcbeanBlog CcbeanBlog
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Ccbean

靡不有初,鲜克有终
首页
  • 前端文章

    • JavaScript
    • HTML+CSS
    • Vue
    • React
  • 系列笔记

    • React使用学习
    • Vue2源码探究
  • Node文章

    • 基础
    • 问题
    • 框架
  • 系列笔记

    • 数据结构与算法
  • 构建工具文章

    • webpack
  • 系列笔记

    • Webpack5使用学习
  • MySQL
  • Linux
  • 网络
  • 小技巧
  • 杂记
  • 系列笔记

    • Protobuf Buffers
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 认识数据结构与算法
  • 数据结构与算法-数组
  • 数据结构与算法-栈
  • 数据结构与算法-队列
  • 数据结构与算法-链表
  • 数据结构与算法-双向链表
  • 数据结构与算法-集合
  • 数据结构与算法-字典
  • 数据结构与算法-哈希表
  • 数据结构与算法-树
  • 数据结构与算法-二叉搜索树
  • 数据结构与算法-红黑树(一)
  • 数据结构与算法-红黑树(二)
    • 二叉搜索树删除操作
    • 一个简单的红黑树推论
    • 红黑树的删除操作
      • 删除单个红色子节点
      • 删除单个黑色子节点
      • 情况1 - 黑兄弟,右红侄
      • 情况2 - 黑兄弟,左红侄
      • 情况3 - 黑兄弟,双黑侄
      • 情况4 - 红兄弟
      • 视角上移
      • 待删除节点是父节点的右子节点
      • 情况1镜像:黑兄弟,左红侄
      • 情况2镜像:黑兄弟,右红侄
      • 情况3镜像:黑兄弟,双黑侄
      • 情况4镜像:红兄弟
    • 封装实现
  • 数据结构与算法-二叉堆
  • 数据结构与算法-图
  • 数据结构与算法-排序
  • 数据结构与算法-总结
  • JavaScript数据结构与算法
ccbean
2022-01-09
目录

数据结构与算法-红黑树(二)

# 数据结构与算法-红黑树(二)

红黑树的规则:

  • 规则1:每个节点的颜色不是红色的,就是黑色的
  • 规则2:根节点是黑色的
  • 规则3:每个叶子节点Nil都是黑色的
  • 规则4:不能连续两红
  • 规则5:任一节点的左右子树黑高相等

# 二叉搜索树删除操作

我们先来简单回顾下二叉搜索树的删除操作。假如我们有一个搜索二叉树(省略Nil节点)如下:

红黑树30

二叉搜索树中删除节点有3种场景:

  • 场景 1:删除节点无子节点,可以直接删除。如删除6。
  • 场景 2:删除节点只有一个子节点,则将父结点的指向它的孩子。如删除8,然后7指向8的子节点9即可。
  • 场景 3:删除节点有两个子节点,可以用前驱(后继)节点替换删除节点。如删除15,找到15的前驱节点9,交换15与9的位置,然后删除15即可,此时删除15就退化成了删除单个节点。

红黑树31

所以删除节点有两个子节点时,交换节点与前驱(后继)节点位置,然后再删除节点。

总结:所有删除,最后都会退化成:

  • **删除单个子节点:**待删除节点有两个子(树)节点 或 待删除节点没有子节点
  • 删除只有一个子树的节点: 待删除节点有一个子(树)节点

那么在二叉搜索树中,就不需要考虑删除的节点有两个双子树的情况,只需要考虑删除单个节点或删除节点只有一个子树。

# 一个简单的红黑树推论

在红黑树中,我们先来看一个简单的推论:如果待删除的节点D只有一个子树节点,那么待删除节点一定是黑色,且唯一的子树一定是单个红色节点。否则都会违反红黑树规则。

红黑树32

其它情况下,均不满足红黑树的规则:

  • D、S都为红色,违反规则4
  • D黑S黑、D红S黑,违反规则5
  • S不是单个红节点而是一个子树,更是违反规则5

那么我们只需要交换D和S的值,删除D即可。

总结:在红黑树中所有删除,最后都会退化成删除单个子节点

# 红黑树的删除操作

红黑树删除分为两个部分:

  • 一是按照二叉搜索树删除对应节点
  • 二是删除之后的自平衡。

二叉树的删除操作我们已分析过,即只需要考虑删除单个节点的情况,下面来看自平衡的维护。

# 删除单个红色子节点

解决:直接删除红色节点即可

删除的节点是红色节点,删除之后不影响红黑树的平衡,结束处理。

# 删除单个黑色子节点

删除单个黑色子节点,则会破坏树的平衡。

如果待删除结点为黑色,这种情况下,不可能通过涂色的方式弥补缺少的黑色,所以要判断其兄弟和侄子的状况,希望通过兄弟那边分支的旋转,来保持黑色的数量。

可归纳为以下4种情况:

  • 黑兄弟,右红侄
  • 黑兄弟,左红侄
  • 黑兄弟,双黑侄
  • 红兄弟

情况 1 到情况 4 是针对删除单个黑色子节点,且待删除节点是父节点的左孩子的分析。

# 情况1 - 黑兄弟,右红侄

黑兄弟,右红侄

这种情况优先级最高,此时:

  • 无视兄弟的左节点颜色以及是否存在;兄弟的左子节点一定是红色或不存在(Nil),如果是黑色则违反规则5。
  • 也无视父节点颜色。

解决:左旋父,祖染父色,父叔黑。

父节点为红色时:

  • 首先,以P为支点进行左旋转
  • 然后以待删除节点D为参照,祖父节点B染父节点P的颜色,B变成红色
  • 待删除节点D的父节点P和叔叔节点BR变成黑色。
  • 最后,删除D节点。BL可为红色或Nil节点

红黑树33

父节点为黑色时:

  • 首先,以P为支点进行左旋转
  • 然后以待删除节点D为参照,祖父节点B染父节点P的颜色,B变成黑色;(B已经是黑色了,为了统一逻辑,还是进行此项操作)
  • 待删除节点D的父节点P(已是黑色)和叔叔节点BR变成黑色
  • 最后,删除D节点。BL可为红色或Nil节点

红黑树34

# 情况2 - 黑兄弟,左红侄

黑兄弟,左红侄

此时:

  • 兄弟右侄子因为不是红的(情况1之外),所以一定不存在(Nil)。如果存在且是黑色,则违反规则5

解决:右旋兄,交换兄弟与其右子颜色,变成情况1

无视父节点颜色:

  • 首先以兄弟节点B为支点右旋转
  • 以D为参照,交换兄弟节点BL和其右子节点B的颜色,此时回归情况1:黑兄弟右红侄

红黑树35

# 情况3 - 黑兄弟,双黑侄

黑兄弟,双黑侄

此时,两个侄子节点一定不存在(Nil)。如果存在,则违反规则5

解决:兄弟红,向上找,遇根或红节点,染黑即解决;

如果父节点P是根节点或红色,染成黑色即可解决:

  • 首先将兄弟节点B染成红色,此时视角在D、B层
  • 然后向上找,视角上移到P层,P为红色,变成黑色即可修复平衡

红黑树36

如果父节点P是黑色:

  • 首先将兄弟节点B染成红色,此时视角在D、B层
  • 然后向上找,视角上移到P层,P为黑色,需要根据兄弟节点PB以及侄子节点分情况处理,总会回归到这几种情况。然后再根据对应情况处理即可。具体分析详见视角上移。

红黑树40

# 情况4 - 红兄弟

红兄弟

此时,父节点一定是黑节点。

解决:左旋父,父、祖换色,变成前3种情况

处理:

  • 首先以父节点P为支点左旋转
  • 然后交换父节点P和祖父节点B的颜色此时BL节点情况

红黑树37

此时,以目标节点D为参照,根据黑兄弟BL以及侄子节点情况可能变成的情况:

  • 回归情况1:黑兄弟,右红侄
  • 回归情况2:黑兄弟,左红侄
  • 回归情况3:黑兄弟,双黑侄

红黑树38

# 视角上移

在情况3,黑兄弟,双黑侄中,如果上移视角后,父节点为黑色,需要根据兄弟节点以及侄子节点分情况处理。

本质上,删除黑节点时,只有情况1和情况3父节点是红色或根节点时可以解决平衡。其它情况都会向这两种情况转化。

上移视角遇到的情况:

  • 情况1:黑兄弟,右红侄

    • 解决:左旋父,祖染父色,父叔黑

    图中省略了父节点是黑色的情况。

    红黑树41

  • 情况2:黑兄弟,左红侄

    • 解决:右旋兄,交换兄弟与其右子颜色,变成情况1

    红黑树42

  • 情况3:黑兄弟,双黑侄

    • 解决:兄弟红,向上找,遇根或红节点,染黑即解决

    红黑树43

  • 情况4:红兄弟

    • 解决:左旋父,父、祖换色
    • 此时P的兄弟为黑,回归情况3

    红黑树44

那么至此,删除节点已分析完毕。待删除节点是父节点的右孩子的,正好是情况 3-6 的镜像,见下面图片,可自行分析。

# 待删除节点是父节点的右子节点

# 情况1镜像:黑兄弟,左红侄

解决:右旋父,祖染父色,父叔黑

红黑树45

# 情况2镜像:黑兄弟,右红侄

解决:左旋兄,交换兄弟与其左子颜色,变成情况1镜像

红黑树46

# 情况3镜像:黑兄弟,双黑侄

解决:兄弟红,向上找,遇根或红节点,染黑即解决;

兄弟D染红后,如果父节点P是根节点或红色节点,染成黑色即可

红黑树47

兄弟D染红后,如果父节点P是黑色,则会回归其它镜像情况,同情况3原理相同,不再赘述。

# 情况4镜像:红兄弟

解决:右旋父,父、祖换色,变成前3种镜像情况

红黑树48

此时,以目标节点D为参照,根据黑兄弟BR以及侄子节点情况可能变成的情况:

  • 回归情况1镜像:黑兄弟,左红侄
  • 回归情况2镜像:黑兄弟,右红侄
  • 回归情况3镜像:黑兄弟,双黑侄

红黑树49

# 封装实现

下面是完整实现。

const COLOR = {
  RED: 'red',
  BLACK: 'black'
}

class Node {
  constructor (key, value, color) {
    this.key = key // 节点对应的key
    this.value = value
    this.color = color

    this.left = null // 指向的左子树
    this.right = null // 指向的右子树
    this.parent = null // 指向的父节点
  }
}


/**
 * 红黑树
 */
 module.exports = class RedBlackTree {
  constructor () {
    this.root = null
  }

  // 先序遍历
  preOrderTraversal (handler) {
    this._preOrderTraversalNode(this.root, handler)
  }
  _preOrderTraversalNode (node, handler) {
    if (node !== null) {
      // 首先回调 handler
      handler(node.key)
      // 然后遍历所有的左子树
      this._preOrderTraversalNode(node.left, handler)
      // 最后遍历所有的右子树
      this._preOrderTraversalNode(node.right, handler)
    }
  }

  // 中序遍历
  inOrderTraversal (handler) {
    this._inOrderTraversalNode(this.root, handler)
  }
  _inOrderTraversalNode (node, handler) {
    if (node !== null) {
      // 首先遍历所有的左子树
      this._inOrderTraversalNode(node.left, handler)
      // 然后回调 handler
      handler(node.key)
      // 最后遍历所有的右子树
      this._inOrderTraversalNode(node.right, handler)
    }
  }

  // 后序遍历
  postOrderTraversal (handler) {
    this._postOrderTraversalNode(this.root, handler)
  }
  _postOrderTraversalNode (node, handler) {
    if (node !== null) {
      // 首先遍历所有的左子树
      this._postOrderTraversalNode(node.left, handler)
      // 然后遍历所有的右子树
      this._postOrderTraversalNode(node.right, handler)
      // 最后回调 handler
      handler(node.key)
    }
  }

  // 搜索值 循环
  search (key) {
    let node = this.root

    while (node !== null) {
      if (node.key > key) { // 向左子树查找
        node = node.left
      } else if (node.key < key) { // 向右子树查找
        node = node.right
      } else {
        return node
      }
    }

    return null
  }

  // 最小值
  min () {
    let node = this.root
    // 循环找到最左子节点
    while (node.left !== null) {
      node = node.left
    }
    return node
  }

  // 最大值
  max () {
    let node = this.root
    // 循环找到最右子节点
    while (node.right !== null) {
      node = node.right
    }
    return node
  }

  /**
   * 左旋
   *      G             P
   *     / \           / \
   *    U   P   ===>  G   N
   *       / \       / \
   *      B   N     U   B
   * @param {*} node 旋转支点G
   */
  rotateLeft (gnode) {
    const pnode = gnode.right

    // G.right = B
    gnode.right = pnode.left
    if (pnode.left) {
      // B节点存在,将B.parent指向G
      pnode.left.parent = gnode
    }

    // P.parent = G.parent
    pnode.parent = gnode.parent

    if (gnode.parent === null) { // G是根节点
      // this.root = P
      this.root = pnode
    } else {
      if (gnode.parent.right === gnode) {
        // G是父节点的右子节点
        gnode.parent.right = pnode
      } else {
        // G是父节点的左子节点
        gnode.parent.left = pnode
      }
    }

    // P.left = G
    pnode.left = gnode
    // G.parent = P
    gnode.parent = pnode
  }

  /**
   * 右旋
   *      G             P
   *     / \           / \
   *    P   U   ===>  N   G
   *   / \               / \
   *  N   B             B   U
   * @param {*} node 旋转支点G
   */
  rotateRight (gnode) {
    const pnode = gnode.left

    // G.left = B
    gnode.left = pnode.right
    if (pnode.right) {
      // B节点存在,将B.parent指向G
      pnode.right.parent = gnode
    }

    pnode.parent = gnode.parent

    if (gnode.parent === null) { // G是根节点
      this.root = pnode
    } else {
      if (gnode.parent.right === gnode) {
        // G是父节点的右子节点
        gnode.parent.right = pnode
      } else {
        // G是父节点的左子节点
        gnode.parent.left = pnode
      }
    }

    // P.right = G
    pnode.right = gnode
    // G.parent = P
    gnode.parent = pnode
  }

  /**
   * 向树中插入一个节点 循环
   */
  insert (key, value) {
    const newNode = new Node(key, value, COLOR.RED)
    if (this.root === null) {
      this.root = newNode
    } else {
      let parent
      let node = this.root
      while(node !== null) {
        parent = node
        if (node.key === newNode.key) {
          // 情况8 插入节点已存在 更新节点
          // newNode.color = node.color
          node.value = newNode.value
          return
        } else if (node.key > newNode.key) {
          // 向左子树查找
          node = node.left
        } else {
          // 向右子树查找
          node = node.right
        }
      }

      newNode.parent = parent
      if (parent.key > newNode.key) {
        parent.left = newNode
      } else {
        parent.right = newNode
      }
    }

    // 插入节点平衡修正
    this.balanceInsertion(newNode)
  }

  /**
   * 插入节点平衡修正
   */
  balanceInsertion (node) {
    // 情况2:插入的新节点N的父节点P为黑色节点,此时不需要任何变化 无需修正

    // 插入节点非根节点 且 插入节点的父级是红色节点
    while (node.parent !== null && node.parent.color === COLOR.RED) {
      let uncle = null
      let grandpa = node.parent.parent
      if (node.parent === grandpa.left) { // 父节点是祖父节点的左子节点
        uncle = grandpa.right
        // 情况3 父红叔红祖黑 叔叔节点是红色
        if (uncle !== null && uncle.color === COLOR.RED) {
          // 父节点、叔叔节点变成黑色,祖父节点变成红色
          node.parent.color = COLOR.BLACK
          uncle.color = COLOR.BLACK
          grandpa.color = COLOR.RED
          // 以祖父节点当作新节点继续调用修正方法
          node = grandpa
          continue
        }

        // 情况5 父节点是祖父节点的左子节点 插入节点是父节点的右子节点 且 父红叔黑祖黑
        if (node === node.parent.right) {
          // 左旋之后,原插入节点的父节点变成新插入节点 回归情况4
          node = node.parent // 将原插入节点的父节点看作是新插入节点
          grandpa = node.parent.parent // 重新赋值新节点的grandpa
          this.rotateLeft(node) // 以父节点为支点进行左旋转
        }

        // 情况4 父节点是祖父节点的左子节点 插入节点是父节点的左子节点 且 父红叔黑祖黑
        // 以及情况5左旋转之后
        node.parent.color = COLOR.BLACK // 父节点变成黑色
        grandpa.color = COLOR.RED // 祖父节点变成红色
        this.rotateRight(grandpa) // 以祖父节点进行右旋转
      } else { // 父节点是祖父节点的右节点
        uncle = grandpa.left
        // 情况3 父红叔红祖黑 叔叔节点是红色
        if (uncle !== null && uncle.color === COLOR.RED) {
          node.parent.color = COLOR.BLACK
          uncle.color = COLOR.BLACK
          grandpa.color = COLOR.RED
          // 以祖父节点当作新节点继续调用修正方法
          node = grandpa
          continue
        }

        // 情况5镜像 父节点是祖父节点的右子节点 插入节点是父节点的左子节点 且 父红叔黑祖黑
        if (node === node.parent.left) {
          // 右旋之后,原插入节点的父节点变成新插入节点 回归情况4镜像
          node = node.parent // 将原插入节点的父节点看作是新插入节点
          grandpa = node.parent.parent // 重新赋值新节点的grandpa
          this.rotateRight(node) // 以父节点为支点进行右旋转
        }

        // 情况4镜像 父节点是祖父节点的右子节点 插入节点是父节点的右子节点 且 父红叔黑祖黑
        if (node === node.parent.right) {
          node.parent.color = COLOR.BLACK
          grandpa.color = COLOR.RED
          this.rotateLeft(grandpa) // 以祖父节点进行左旋转
        }
      }
    }

    // 情况1:插入节点是根节点
    // 情况3:最后回归情况1
    this.root.color = COLOR.BLACK
  }

  /**
   * 查找后继节点
   */
  getSuccessor (node) {
    while (node.left !== null) {
      node = node.left
    }
    return node
  }

  /**
   * 删除一个节点
   */
  delete (key) {
    let current = this.root // 待删除节点

    while (current) {
      if (current.key > key) {
        current = current.left
      } else if (current.key < key) {
        current = current.right
      } else if (current.key === key) {
        break
      }
    }
    // 没有找到待删除节点,直接返回false
    if (current === null) return false

    this._deleteNode(current)
  }
  _deleteNode (current) {
    // 删除节点处理
    if (current.left !== null && current.right !== null) { // 待删除节点有两个子节点
      const successor = this.getSuccessor(current.right) // 查找后继节点
      current.key = successor.key // 交换值
      current.value = successor.value // 交换值

      this._deleteNode(successor) // 递归删除后继节点 最多递归一次(后继节点无子节点或只有一个右红子节点)
    } else { // 待删除节点是单个节点 或 只有一个子树节点
      /**
       * 命中逻辑:
       *  1. 待删除节点是单个节点
       *  2. 待删除节点只有一个子树节点
       *  3. 待删除节点有两个节点,递归命中:
       *     - 后继节点是黑色,有一个红色的右子节点
       *     - 后继节点是单个节点
       */
      let delNode = null // 实际被删除的节点 可能是待删除节点 或 待删除节点的子节点
      let isLeftChild = false // delNode是否是父节点的左子节点
      // 待删除节点有一个子节点 待删除节点一定为黑色,子节点为红色
      if (current.left) {
        // 待删除节点有左子节点
        current.key = current.left.key // 交换值
        current.value = current.left.value
        delNode = current.left // 标记实际被删除节点
        isLeftChild = true
        current.left = null
      } else if (current.right) {
        // 待删除节点有右子节点
        current.key = current.right.key // 交换值
        current.value = current.right.value
        delNode = current.right // 标记实际被删除节点
        isLeftChild = false
        current.right = null
      } else {
        // 待删除节点没有子节点 那么待删除节点就是实际被删除节点
        if (current.parent.left === current) {
          isLeftChild = true
          current.parent.left = null
        } else {
          isLeftChild = false
          current.parent.right = null
        }
        delNode = current
      }

      if (current.parent === null && current === delNode) { // 空树
        // 待删除节点是根节点 且 待删除节点是实际被删除节点  说明待删除的根节点没有子节点,清空树
        this.root = null
      } else {
        // 删除节点后平衡修正
        this.balanceDeletion(delNode, isLeftChild)
      }
    }
  }

  /**
   * 删除节点后平衡修正
   * @param {*} node 平衡的参照节点 传入的节点已被删除
   * @param {*} isLeftChild 已被删除的节点是否是父节点的左子节点
   */
  balanceDeletion (node, isLeftChild) {
    const deletedNode = node // 已被移除节点
    // 删除根节点
    while (node !== this.root && node.color === COLOR.BLACK) {
      const nodeParent = node.parent
      if ((isLeftChild && deletedNode === node) || node.parent.left === node) { // 参照节点是父节点的左子节点
        const nodeBrother = nodeParent.right // 兄弟节点

        if (nodeBrother.color === COLOR.BLACK) {
          // 黑兄弟
          if (nodeBrother.right !== null && nodeBrother.right.color === COLOR.RED) {
            // 黑兄弟,右红侄
            // 解决:左旋父,祖染父色,父叔黑
            this.rotateLeft(nodeParent)
            nodeParent.parent.color = nodeParent.color // 祖染父色
            nodeParent.color = COLOR.BLACK // 父黑
            node.parent.parent.right.color = COLOR.BLACK // 叔黑
            break
          } else if (nodeBrother.left !== null && nodeBrother.left.color === COLOR.RED) {
            // 黑兄弟,左红侄
            // 解决:右旋兄,交换兄弟与其右子颜色,变成 黑兄弟,右红侄
            this.rotateRight(nodeParent.right)
            nodeParent.right.color = COLOR.BLACK // 兄弟颜色
            nodeParent.right.right.color = COLOR.RED // 兄弟右子颜色
            // 回归 黑兄弟,右红侄 循环处理
            continue
          } else {
            // 黑兄弟,双黑侄
            // 解决:兄弟红,向上找,遇根或红节点,染黑即解决
            nodeBrother.color = COLOR.RED // 兄弟红
            node = node.parent // 上移视角 以父为参照节点
            continue
          }
        } else {
          // 红兄弟
          // 解决:左旋父,父、祖换色,变成上面3种情况
          this.rotateLeft(nodeParent)
          nodeParent.color = COLOR.RED
          nodeParent.parent.color = COLOR.BLACK
          continue
        }
      } else { // 参照节点是父节点的右子节点
        const nodeBrother = nodeParent.left // 兄弟节点

        if (nodeBrother.color === COLOR.BLACK) {
          // 黑兄弟
          if (nodeBrother.left !== null && nodeBrother.left.color === COLOR.RED) {
            // 黑兄弟,左红侄
            // 解决:右旋父,祖染父色,父叔黑
            this.rotateRight(nodeParent)
            nodeParent.parent.color = nodeParent.color
            nodeParent.color = COLOR.BLACK
            node.parent.parent.left.color = COLOR.BLACK
            break
          } else if (nodeBrother.right !== null && nodeBrother.right.color === COLOR.RED) {
            // 黑兄弟,右红侄
            // 解决:左旋兄,交换兄弟与其左子颜色 变成 黑兄弟,左红侄
            this.rotateLeft(node.parent.left)
            node.parent.left.color = COLOR.BLACK // 兄弟颜色
            node.parent.left.left.color = COLOR.RED // 左子颜色
            continue
          } else {
            // 黑兄弟,双黑侄
            // 解决:兄弟红,向上找,遇根或红节点,染黑即解决
            nodeBrother.color = COLOR.RED
            node = node.parent // 上移视角 以父为参照节点
            continue
          }
        } else {
          // 红兄弟
          // 解决:右旋父,父、祖换色,变成前3种镜像情况
          this.rotateRight(nodeParent)
          nodeParent.color = COLOR.RED
          nodeParent.parent.color = COLOR.BLACK
          continue
        }
      }
    }

    // 删除只有一个子树的节点 或 情况3:遇根或红,染黑
    node.color = COLOR.BLACK
  }
}

测试:

const RedBlackTree = require('../../lib/RedBlackTree')

const rbt = new RedBlackTree()

rbt.insert(10, '十')
rbt.insert(9, '九')
rbt.insert(8, '八')
rbt.insert(7, '七')
rbt.insert(6, '六')
rbt.insert(5, '五')
rbt.insert(4, '四')
rbt.insert(3, '三')
rbt.insert(2, '二')
rbt.insert(1, '一')
console.log(rbt)

// 删除
// rbt.delete(1) // 单个红
// rbt.delete(2) // 有一个子树节点
// rbt.delete(4) // 父右子:黑兄弟,右红值
// rbt.delete(6) // 父右子:红兄弟
// rbt.delete(7) // 双子树节点

const rbt2 = new RedBlackTree()

// insert
rbt2.insert(1, '一')
rbt2.insert(2, '二')
rbt2.insert(3, '三')
rbt2.insert(4, '四')
rbt2.insert(5, '五')
rbt2.insert(6, '六')
rbt2.insert(7, '七')
rbt2.insert(8, '八')
rbt2.insert(9, '九')
rbt2.insert(10, '十')
console.log(rbt2)

// 删除
rbt2.delete(5)

console.log(rbt2)

// 中序遍历
let inString2 = ""
rbt2.inOrderTraversal((key) => {
  inString2 += `${key} `
})
console.log(inString2)
编辑 (opens new window)
上次更新: 2022/01/13, 18:35:31
数据结构与算法-红黑树(一)
数据结构与算法-二叉堆

← 数据结构与算法-红黑树(一) 数据结构与算法-二叉堆→

最近更新
01
阅读精通正则表达式总结
09-29
02
项目搭建规范的配置
07-15
03
Vite的使用
07-03
更多文章>
Theme by Vdoing | Copyright © 2018-2023 Ccbeango
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式