红黑树的实现原理及应用

@OTC

什么是红黑树

红黑树(R-B Tree, 全称 Red-Black Tree)是一种特殊的二叉查找树, 其中每个节点都有颜色, 红色或者黑色.
红黑树的特性:

  1. 树的节点是黑色或者红色
  2. 树的根节点和指向null的叶子节点都是黑色
  3. 不能有两个红色节点是连续的
  4. 每个节点至为null的子节点的任何路径, 都含有相同数量的黑色节点

示例:

             8B
            / \
           4R  9B
          /  \
        2B    6B
       /  \   / \
      1R  3R 5R  7R

红黑树的应用和时间复杂度

  1. 主要是 Java 中的 TreeMap 和 TreeSet. jdk1.8 之后, HashMap 的table中的链表长度大于8的时候也是用 红黑树.
  2. 时间复杂度: 查找, 插入, 删除都可以在 O(log n) 内完成. 且节点数为 n 的数高度最大为 2log(n+1).

红色树的操作

节点的基本定义
    //节点定义
    public class RBTNode<T extends Comparable<T>> {
        public boolean isBlack;
        public T key;
        public RBTNode<T> parent;
        public RBTNode<T> left;
        public RBTNode<T> right;

        public RBTNode(T key, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right, boolean isBlack) {
            this.isBlack = isBlack;
            this.key = key;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }
    
        public String toString() {
            return "key: " + key + (isBlack ? " B " : " R ") +
                    (parent != null ? (
                            ((parent.left != null && parent.left == this) ?
                                    parent.key + " Left" :
                                    (parent.right != null && parent.right == this) ?
                                    parent.key + " Right" : ""))
                            : "");
        }
    }
旋转 - 左旋转的两种情况

旋转的中心思想是: 从新设置 x 节点, y 节点的 左 父 右 节点, 并设置关联变化的节点的父节点.

    px                   px             |                px                    px                     
     \                    \             |               /                     /                  
      x                    y            |              x           ->        y                                 
     / \      ->          / \           |             / \                   / \                       
   lx   y                x  ry          |            lx  y                 x   ry                        
       / \              / \             |               / \               / \                           
     ly  ry            lx  ly           |              ly  ry            lx ly                       

    // 对 x 进行左旋转, 将 x 是 y 的父节点变成, y 是 x 的父节点.
    private void rotateLeft(RBTNode<T> x) {
        RBTNode<T> y = x.right;
        // 1. 设置x 的 左 父 右 节点
        // 1-1 x的左节点无变化
        // 1-2 x的右节点, 设置x右节点的父节点
        x.right = y.left;
        if(y.left != null) y.left.parent = x;
        // 1-3 x的父节点无变化

        //2. 设置y 的 左 父 右 节点
        //2-1: 设置y的父节点
        y.parent = x.parent;
 
        if (x.parent == null) this.root = y;
        else if (x.parent.left == x) x.parent.left = y;
        else x.parent.right = y;

        //2-2: 设置y 的左节点
        y.left = x;
        x.parent = y;

        //2-3: y节点的右节点无变化
    }
 
旋转 - 右旋转的两种情况
          py                py         |              py                   py           
         /                 /           |               \                    \         
        y                 x            |                y        ->          x        
       / \        ->     / \           |               / \                  / \       
      x   ry           lx   y          |              x  ry               lx   y      
     / \                   / \         |             / \                      / \     
    lx rx                 rx  ry       |            lx rx                    rx  ry   

    // 对 y 进行右旋转, 将 y 是 x 的父节点 变成 x 是 y 的父节点                                                                                                                  
    private void rotateRight(RBTNode<T> y) {
        RBTNode<T> x = y.left;

        //1: 设置y 的左 父 右 节点
        //1-1: 设置y 的左节点, 并设置其父节点
        y.left = x.right;
        if (x.right != null) x.right.parent = y;

        //2-2: 设置x的父节点
        x.parent = y.parent;
        //1-3: y的右节点无变化

        //2: 设置x 的左 右 父节点

        //2-2: 设置x 的父节点, 及其父节点的子节点
        if (y.parent == null) this.root = x;
        else if (y == y.parent.right) y.parent.right = x;
        else y.parent.left = x;

        //2-1: 设置 x的右节点, 并设置其父节点
        x.right = y;
        //1-2: 设置y 的父节点
        y.parent = x;
        //2-3 x的左节点无变化
    }

插入并修正
  1. 若root 是null , 则插入节点就是root 节点.
  2. 若root 不是null, 则向下查找node 的父节点, 并根据与父节点的大小关系, 写入待插入节点. 插入的节点都是红色
  3. 修正插入的树.
  4. 修正过程, 修正的核心思想是: 将红色节点移动到根节点, 将根节点设为黑色.
违背原则4现象说明处理策略
情况1isRed(parent), isRed(uncle)1. setBlack(parent);
2. setBlack(uncle);
3. setRed(grandParent);
4. current = grandParent;
5. 检测 current 是否违背原则并处理
情况2isRed(parent), isBlack(uncle), uncle == grandParent.left, current == parent.right1. setBlack(parent);
2. setRed(grandParent);
3. rotateLeft(grandParent);
情况3isRed(parent), isBlack(uncle), uncle == grandParent.left, current == parent.left1. current = parent;
2. rotateRight(current);
3. 检测 current 是否违背原则并处理
情况4isRed(parent), isBlack(uncle), uncle == grandParent.right, current == parent.right1. current = parent;
2. rotateLeft(current);
3. 检测 current 是否违背原则并处理
情况5isRed(parent), isBlack(uncle), uncle == grandParent.right, current == parent.left1. setBlack(parent);
2. setRed(grandParent);
3. rotateRight(grandParent);

    // 插入修正
    private void insertFix(RBTNode<T> node) {
        RBTNode<T> parent;
        RBTNode<T> grandParent;
        while (isRed(parentOf(node))) {
            parent = parentOf(node);
            grandParent = parentOf(parent);
            RBTNode<T> uncle;
            if (parent == grandParent.left) {
                uncle = grandParent.right;
                if (isRed(uncle)) {  // 情况 1
                    setBlack(parent);
                    setBlack(uncle);
                    setRed(grandParent);
                    node = grandParent;
                    continue;
                }
                if (isBlack(uncle)) {
                    if (parent.right == node) { // 情况 4
                        node = parent;
                        rotateLeft(grandParent);
                    } else { // 情况 5
                        setBlack(parent);
                        setRed(grandParent);
                        rotateRight(grandParent);
                    }
                }
            } else {
                uncle = grandParent.left;
                if (isRed(uncle)) { // 情况 1
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(grandParent);
                    node = grandParent;
                    continue;
                }
                if (isBlack(uncle)) {
                    if (parent.left == node) { // 情况 2
                        node = parent;
                        rotateRight(node);
                    } else { // 情况 3
                        setBlack(parent);
                        setRed(grandParent);
                        rotateLeft(grandParent);
                    }
                }
            }
        } 
    }   

    // 插入
    public void insert(RBTNode<T> node) {
        if (this.root == null) {
            this.root = node;
        } else {
            int compare = 0;
            RBTNode<T> temp = null;
            RBTNode<T> current = this.root;

            //1. 先查找二叉树, 确定node的插入位置
            while (current != null) {
                temp = current;
                compare = node.getKey().compareTo(current.getKey());
                if (compare < 0) current = current.getLeft(); 
                else current = current.getRight(); 
            }

            node.setParent(temp);
            compare = node.getKey().compareTo(temp.getKey());
            if (compare < 0) temp.setLeft(node);
            else temp.setRight(node);
        } 
        insertFix(node);
        setBlack(root);
    } 
 
删除并修正
  1. 核心思想: 将被删节点所包含的额外黑色节点(右节点)不断往根节点方向移动并按照情况修正
  2. 修正的方法
情况现象说明处理策略
情况1isRed(current)&&isBlack(brother) or isRed(brother)&&isBlack(current)1. setBlack(current);
2. 执行核心思想移动其子节点
情况2isBlack(current) isBlack(brother) current==root执行核心思想移动子节点
情况3isBlack(current) isBlack(brother) current!=root执行下面4中情况
情况3-1isRed(brother)1. setBlack(brother);
2. setRed(parent);
3. rotateLeft(parent);
4. 重新设置current的brother
情况3-2isBlack(brother), isBlack(brother.left), isBlack(brother.right)1. setRed(brother);
2. current = parent
情况3-3isBlack(brother), isBlack(brother.right), isRed(brother.left)1. setBlack(brother.left);
2. setRed(brother);
3. rotateRight(brother);
4. 重新设置current的brother
情况3-4isBlack(brother), isRed(brother.right)1. brother.color = parent.color;
2. setBlack(parent);
3. setBlack(brother.right);
4. rotateLeft(brother);
5. root = x

完整实现

public class RBTree<T extends Comparable<T>> {

    private RBTNode<T> root;

    public void preOrder() {
        preOrder(root);
    }

    private void preOrder(RBTNode<T> node) {
        if (node != null) {
            System.out.println(node + " ");
            preOrder(node.left);
            preOrder(node.right);
        }
    }

    public void inOrder() {
        inOrder(root);
    }

    private void inOrder(RBTNode<T> node) {
        if (node != null) {
            inOrder(node.left);
            System.out.println(node.toString());
            inOrder(node.right);
        }
    }

    public void postOrder() {
        postOrder(root);
    }

    private void postOrder(RBTNode<T> node) {
        if (node != null) {
            preOrder(node.left);
            preOrder(node.right);
            System.out.println(node + " ");
        }
    }

    public void insert(T key) {
        insert(new RBTNode<T>(key, null, null, null, false));
    }

    private void insert(RBTNode<T> node) { 
    }

    private RBTNode<T> parentOf(RBTNode<T> x) {
        return (x == null ? null : x.parent);
    }

    private boolean isRed(RBTNode<T> x) {
        if (x == null) return false;
        return !x.isBlack;
    }

    private boolean isBlack(RBTNode<T> x) {
        return (x == null || x.isBlack);
    }

    private void setRed(RBTNode<T> x) {
        if (x != null)
            x.isBlack = false;
    }

    private void setBlack(RBTNode<T> x) {
        if (x != null)
            x.isBlack = true;
    }

    private void insertFix(RBTNode<T> node) { 
    }

    private void rotateRight(RBTNode<T> y) {
    }

    private void rotateLeft(RBTNode<T> x) { 
    }

    public void remove(RBTNode<T> node) {
    }
}

//测试代码
public static void main(String[] args) {
        RBTree<Integer> tree = new RBTree<>();
        int[] arr = new int[]{9, 8, 3, 5, 4, 2, 7, 6, 1};
        for (int item : arr)
            tree.insert(9);
        System.out.println("前序");
        tree.preOrder();
        System.out.println("----------------------");
        System.out.println("中序");
        tree.inOrder();
        System.out.println("----------------------");
        System.out.println("后序");
        tree.postOrder();
    }

// 前序
// key: 8 B  
// key: 4 R 8 Left 
// key: 2 B 4 Left 
// key: 1 R 2 Left 
// key: 3 R 2 Right 
// key: 6 B 4 Right 
// key: 5 R 6 Left 
// key: 7 R 6 Right 
// key: 9 B 8 Right 
// ----------------------
// 中序
// key: 1 R 2 Left
// key: 2 B 4 Left
// key: 3 R 2 Right
// key: 4 R 8 Left
// key: 5 R 6 Left
// key: 6 B 4 Right
// key: 7 R 6 Right
// key: 8 B 
// key: 9 B 8 Right
// ----------------------
// 后序
// key: 4 R 8 Left 
// key: 2 B 4 Left 
// key: 1 R 2 Left 
// key: 3 R 2 Right 
// key: 6 B 4 Right 
// key: 5 R 6 Left 
// key: 7 R 6 Right 
// key: 9 B 8 Right 
// key: 8 B  

参考文献

  1. https://sandbox.runjs.cn/show/2nngvn8w
  2. http://www.cnblogs.com/skywang12345/p/3624343.html
  3. http://www.cnblogs.com/skywang12345/p/3245399.html
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值