返回

RBTree,红黑树解读与JS实现

Android

RBTree:红黑树解读与JS实现

红黑树是一种自平衡二叉查找树,因其具备快速检索数据的能力而经常被应用于计算机科学中。不同于普通二叉树,红黑树严格遵守一系列约束条件,确保任何时候都能以最短的时间复杂度完成数据查找、插入或删除操作。

红黑树的特点

  • 符合二叉查找树的所有特性
  • 节点具有两种颜色:红或黑
  • 根节点始终为黑色
  • 所有叶子节点均为黑色(叶子节点为NIL节点)
  • 任意红色节点必须拥有两个黑色子节点
  • 从根节点到任何一个叶子节点的路径中,黑色节点的数量必须相等

RBTree实现

为了帮助您更深入理解红黑树的概念,我们接下来将借助JavaScript来实现一个RBTree。首先,定义一个RBTree类,其中包含用于操作树的各种方法:

class RBTree {
    constructor() {
        this.root = null;
    }

    // 插入一个新节点
    insert(key, value) {
        // 创建一个新的节点
        let newNode = new RBNode(key, value);

        // 如果树为空,将新节点作为根节点
        if (this.root === null) {
            this.root = newNode;
            this.root.color = RBNode.BLACK;
        } else {
            // 将新节点插入到适当的位置
            this._insert(newNode);
        }

        // 调整树以满足红黑树的性质
        this._fixInsert(newNode);
    }

    // 在树中查找一个节点
    search(key) {
        let node = this.root;

        // 循环遍历树,直到找到目标节点或到达叶节点
        while (node !== null) {
            if (key === node.key) {
                return node;
            } else if (key < node.key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }

        // 未找到目标节点
        return null;
    }

    // 删除一个节点
    delete(key) {
        // 查找要删除的节点
        let node = this.search(key);

        // 如果节点不存在,则直接返回
        if (node === null) {
            return;
        }

        // 如果要删除的节点没有子节点,则直接将其删除
        if (node.left === null && node.right === null) {
            this._remove(node);
        } else {
            // 如果要删除的节点只有一个子节点,则将其子节点作为该节点的替代节点
            if (node.left === null) {
                this._replaceNode(node, node.right);
            } else if (node.right === null) {
                this._replaceNode(node, node.left);
            } else {
                // 如果要删除的节点有两个子节点,则找到其后继节点,并将后继节点的值复制到该节点,然后删除后继节点
                let successor = this._findSuccessor(node);
                node.key = successor.key;
                node.value = successor.value;
                this._remove(successor);
            }
        }

        // 调整树以满足红黑树的性质
        this._fixDelete();
    }

    // 获取树的最小值
    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;
    }

    // 获取树的深度
    depth() {
        return this._getDepth(this.root);
    }

    // 私有方法,用于插入一个新节点
    _insert(newNode) {
        // 找到要插入新节点的位置
        let parent = null;
        let node = this.root;

        while (node !== null) {
            parent = node;

            if (newNode.key < node.key) {
                node = node.left;
            } else {
                node = node.right;
            }
        }

        // 将新节点插入到适当的位置
        if (parent === null) {
            this.root = newNode;
        } else if (newNode.key < parent.key) {
            parent.left = newNode;
        } else {
            parent.right = newNode;
        }

        // 设置新节点的颜色为红色
        newNode.color = RBNode.RED;
    }

    // 私有方法,用于调整树以满足红黑树的性质
    _fixInsert(newNode) {
        // 如果新节点的父节点为黑色,则无需调整
        if (newNode.parent === null || newNode.parent.color === RBNode.BLACK) {
            return;
        }

        // 如果新节点的父节点为红色,则需要进行调整
        if (newNode.uncle() === null || newNode.uncle().color === RBNode.BLACK) {
            // 情况1:新节点的父节点为红色,其叔叔节点为黑色或不存在
            // 将新节点的父节点和叔叔节点都染成黑色,并将新节点的祖父节点染成红色
            newNode.parent.color = RBNode.BLACK;
            newNode.uncle().color = RBNode.BLACK;
            newNode.grandparent().color = RBNode.RED;

            // 递归调整祖父节点
            this._fixInsert(newNode.grandparent());
        } else {
            // 情况2:新节点的父节点为红色,其叔叔节点也为红色
            // 将新节点的父节点、叔叔节点和祖父节点都染成黑色
            newNode.parent.color = RBNode.BLACK;
            newNode.uncle().color = RBNode.BLACK;
            newNode.grandparent().color = RBNode.BLACK;

            // 将新节点的祖父节点的父节点作为新的祖父节点,继续调整
            this._fixInsert(newNode.grandparent().parent);
        }
    }

    // 私有方法,用于删除一个节点
    _remove(node) {
        // 如果要删除的节点没有子节点,则直接将其删除
        if (node.left === null && node.right === null) {
            if (node === this.root) {
                this.root = null;
            } else {
                this._replaceNode(node, null);
            }
        } else {
            // 如果要删除的节点只有一个子节点,则将其子节点作为该节点的替代节点
            if (node.left === null) {
                this._replaceNode(node, node.right);
            } else if (node.right === null) {
                this._replaceNode(node, node.left);
            } else {
                // 如果要删除的节点有两个子节点,则找到其后继节点,并将后继节点的值复制到该节点,然后删除后继节点
                let successor = this._findSuccessor(node);
                node.key = successor.key;
                node.value = successor.value;
                this._remove(successor);
            }
        }

        // 调整树以满足红黑树的性质
        this._fixDelete();
    }

    // 私有方法,用于调整树以满足红黑树的性质
    _fixDelete() {
        // 如果要删除的节点是红色,则直接将其删除,无需调整
        if (node.color === RBNode.RED) {
            return;
        }

        // 如果要删除的节点是黑色,则需要进行调整
        let node = node.parent;

        while (node !== null && node.color === RBNode.BLACK) {
            // 情况1:要删除的节点是其父节点的左子节点,且其兄弟节点为红色
            if (node.left === null || node.left.color === RBNode.BLACK) {
                // 将其兄弟节点染成黑色,并将父节点染成红色
                node.right.color = RBNode.BLACK;
                node.color = RBNode