JS数据结构5:树-红黑树

概念

规则

  1. 顾名思义,每个节点不是红的就是黑的
  2. 树的根节点是黑的;
  3. 所有叶节点都是黑的(用NULL 引用表示的节点);
  4. 如果一个节点是红的,那么它的两个子节点都是黑的;
  5. 不能有两个相邻的红节点(一个红节点不能有红的父节点或子节点);
  6. 从给定的节点到它的后代节点(NULL 叶节点)的所有路径包含相同数量的黑色节点。

在这里插入图片描述

代码实现

红黑树仍然是个自平衡二叉搜索树,所以可以继承AVLTree

1 构造红黑结点与红黑树结构

class RedBlackNode extends Node {
    constructor(key) {
        super(key);
        this.key = key;
        this.color = Colors.RED;
        this.parent = null;
    }
    // 判断结点是否为红色
    isRed() {
        return this.color === Colors.RED;
    }
}

class RedBlackTree extends BinarySearchTree {
    constructor() {
        super();
        this.root = null;
    }
}

2 insert 插入

向红黑树插入节点和在二叉搜索树中是一样的。
除了插入的代码,我们还要在插入后给节点应用一种颜色,并且验证树是否满足红黑树的条件以及是否还是自平衡的

insert(key) {
    if (this.root === null) {
        this.root = new RedBlackNode(key);
        this.root.color = Colors.BLACK;
    } else {
        const newNode = this.insertNode(this.root);
        this.fixTreeProperties(newNode);
    }
}
insertNode(node, key) {
    // 小于当前结点
    if (this.compareFn(key, node.key) === Compare.LES_THAN) {
        if (node.left === null) {
            node.left = new RedBlackNode(key);
            node.left.parent = node;
            return node.left;
        } else {
            // 向下递归直到左子结点为null
            return this.insertNode(node.left, key);
        }
    } else if (node.right === null) {
        node.right = new RedBlackNode(key);
        node.right.parent = node;
    } else {
        return this.insertNode(node.right, key);
    }
}

3 重构红黑树

要验证红黑树是否还是平衡的以及满足它的所有要求,我们需要使用两个概念:重新填色和旋转。

  • 在向树中插入节点后,新节点将会是红色。
  • 这不会影响黑色节点数量的规则(规则6),但会影响规则5:两个后代红节点不能共存。
  • 如果插入节点的父节点是黑色,那没有问题。但是如果插入节点的父节点是红色,那么会违反规则5。
  • 要解决这个冲突,我们只需要改变父节点、祖父节点和叔节点(因为我们同样改变了父节点的颜色)
while (node && 
node.parent && 
node.parent.color.isRed() && 
node.color !== Colors.BLACK) {}

情况A:父节点是左侧子节点

  1. 叔节点也是红色——只需要重新填色
  2. 节点是右侧子节点——左旋转
  3. 节点是左侧子节点——右旋转
    在这里插入图片描述
    在这里插入图片描述
   let parent = node.parent;
   const grandParent = parent.parent;
   // 情形A:父节点是左侧子节点
   if (grandParent && grandParent.left === parent) {
       const uncle = grandParent.right;
       // 情形A1:叔节点也是红色——只需要重新填色
       if (uncle && uncle.color === Colors.RED) {
           grandParent.color = Colors.RED;
           parent.color = Colors.BLACK;
           uncle.color = Colors.BLACK;
           node = grandParent;
       } else {
           // 情形A2:节点是右侧子节点——左旋转
           if (node === parent.right) {
               this.rotationRR(parent);
               node = parent;
               parent = node.parent;
           }
           // 情形A3:节点是左侧子节点——右旋转
           this.rotationLL(grandParent);
           parent.color = Colors.BLACK;
           grandParent.color = Colors.RED;
           node = parent;
       }
   } 

情况B:父节点是右侧子节点

  1. 叔节点是红色——只需要重新填色
  2. 节点是左侧子节点——右旋转
  3. 节点是右侧子节点——左旋转
    在这里插入图片描述
    在这里插入图片描述
     const uncle = grandParent.left;
     // 情形B1:叔节点是红色——只需要重新填色
     if (uncle && uncle.color === Colors.RED) {
         grandParent.color = Colors.RED;
         parent.color = Colors.BLACK;
         uncle.color = Colors.BLACK;
         node = grandParent;
     } else {
         // 情形B2:节点是左侧子节点——左旋转
         if (node === parent.left) {
             this.rotationLL(parent);
             node = parent;
             parent = node.parent;
         }
         // 情形B3:节点是右侧子节点——左旋转
         this.rotationRR(grandParent);
         parent.color = Colors.BLACK;
         grandParent.color = Colors.RED;
         node = parent;
     }

4 旋转方式

右旋转

    rotationLL(node) {
        const tmp = node.left;
        node.left = tmp.right;
        if (tmp.right && tmp.right.key) {
            tmp.right.parent = node;
        }
        tmp.parent = node.parent;
        if (!node.parent) {
            this.root = tmp;
        }
        else {
            if (node === node.parent.left) {
                node.parent.left = tmp;
            }
            else {
                node.parent.right = tmp;
            }
        }
        tmp.right = node;
        node.parent = tmp;
    }

左旋转

    rotationRR(node) {
        const tmp = node.right;
        node.right = tmp.left;
        if (tmp.left && tmp.left.key) {
            tmp.left.parent = node;
        }
        tmp.parent = node.parent;
        if (!node.parent) {
            this.root = tmp;
        }
        else {
            if (node === node.parent.left) {
                node.parent.left = tmp;
            }
            else {
                node.parent.right = tmp;
            }
        }
        tmp.left = node;
        node.parent = tmp;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值