一、概述
1. 历史
红黑树是一种自平衡二叉查找树,最早由一名叫Rudolf Bayer的德国计算机科学家于1972年发明。然而,最初的树形结构不是现在的红黑树,而是一种称为B树的结构,它是一种多叉树,可以用于在磁盘上存储大量数据。
在1980年代早期,计算机科学家Leonard Adleman和Daniel Sleator推广了红黑树,并证明了它的自平衡性和高效性。从那时起,红黑树成为了最流行的自平衡二叉查找树之一,并被广泛应用于许多领域,如编译器、操作系统、数据库等。
红黑树的名字来源于红色节点和黑色节点的交替出现,它们的颜色是用来维护树的平衡性的关键。它们的颜色具有特殊的意义,黑色节点代表普通节点,而红色节点代表一个新添加的节点,它们必须满足一些特定的规则才能维持树的平衡性。
红黑树也是一种自平衡的二叉搜索树,较之AVL,插入和删除时旋转次数更少。
2. 红黑树特性
- 所有节点都有两种颜色:红🔴、黑⚫️
- 所有null视为黑色⚫️
- 红色🔴节点不能相邻
- 根节点是黑色⚫️
- 从根到任意一个叶子节点,路径中的黑色⚫️节点数一样
二、实现
1. 插入情况
插入节点均视为红色🔴
case 1:插入节点为根节点,将根节点变黑⚫️
case 2:插入节点的父节点若为黑色⚫️,树的红黑性质不变,无需调整
插入节点的父节点为红色🔴,触发红红相邻
case3:叔叔为红色🔴
- 父亲变为黑色⚫️,为了保证黑色平衡,连带的叔叔也变为黑色⚫️
- 祖父如果是黑色不变,就会造成这棵子树黑色过多,因此祖父节点变为红色🔴
- 祖父如果变成红色,可能会接着触发红红相邻,因此对将祖父进行递归调整
case 4:叔叔为黑色⚫️
1. 父亲为左孩子,插入节点也是左孩子,此时即LL不平衡
- 让父亲变黑⚫️,为了保证这棵子树黑色不变,将祖父变成红🔴,但叔叔子树少了一个黑色
- 祖父右旋,补齐一个黑色给叔叔,父亲旋转上去取代祖父,由于它是黑色,不会再次触发红红相邻
2. 父亲为左孩子,插入节点是右孩子,此时即LR不平衡
- 父亲左旋,变为LL情况,按1.来后续处理
3. 父亲为右孩子,插入节点也是右孩子,此时即RR不平衡
- 让父亲变黑⚫️,为了保证这棵子树黑色不变,将祖父变成红🔴,但叔叔子树少了一个黑色
- 祖父左旋,补齐一个黑色给叔叔,父亲旋转上去取代祖父,由于它是黑色,不会再次触发红红相邻
4. 父亲为右孩子,插入节点是左孩子,此时即RL不平衡
- 父亲右旋,变成RR情况,按3.后续处理

节点类Node
    private static class Node {
        int key;
        Object value;
        Node left;
        Node right;
        Node parent;
        Color color = Color.RED;  // 颜色
        /**
         * 判断是否是左孩子
         * @return
         */
        public boolean isLeftChild() {
            return parent != null && parent.left == this;
        }
        /**
         * 找叔叔节点
         * @return
         */
        public Node uncle() {
            if(parent == null || parent.parent == null) {
                return null;
            }
            if(parent.isLeftChild()) {
                return parent.parent.right;
            } else {
                return parent.parent.left;
            }
        }
        /**
         * 找兄弟节点
         * @return
         */
        public Node sibling() {
            if(parent == null) {  // 根节点
                return null;
            }
            if(this.isLeftChild()) {
                return parent.right;
            } else {
                return parent.left;
            }
        }
    }判断节点的颜色
    /**
     * 判断节点是否是红色
     * @param node
     * @return
     */
    public boolean isRed(Node node) {
        return node != null && node.color == Color.RED;
    }
    /**
     * 判断节点是否为黑色
     * @param node
     * @return
     */
    public boolean isBlack(Node node) {
        return node == null || node.color == Color.BLACK;
    }右旋
右旋前

右旋后

代码:
    private Node root;
    /**
     * 右旋
     * 1. parent的处理
     * 2. 旋转后新根的父子关系
     * @param pink
     */
    private void rightRotate(Node pink) {
        Node parent = pink.parent;
        Node yellow = pink.left;
        Node green = yellow.right;
        // 被旋转节点的右子树不为空
        if(green != null) {
            green.parent = pink;
        }
        yellow.right = pink;
        yellow.parent = parent;
        pink.left = green;
        pink.parent = yellow;
        
        if(parent == null) {
            // 旋转后yellow为根节点
            root = yellow;
        }else if(parent.left == pink) {
            // 左子树
            parent.left = yellow;
        } else {
            // 右子树
            parent.right = yellow;
        }
    }左旋
    /**
     * 左旋
     * @param pink
     */
    private void leftRotate(Node pink) {
        Node parent = pink.parent;
        Node yellow = pink.right;
        Node green = yellow.left;
        if(green != null) {
            green.parent = pink;
        }
        yellow.left = pink;
        yellow.parent = parent;
        pink.right = green;
        pink.parent = yellow;
        if(parent == null) {
            root = yellow;
        } else if(parent.left == pink) {
            parent.left = yellow;
        } else {
            parent.right = yellow;
        }
    }新增节点
    /**
     * 新增或更新
     * 正常增、遇到红红不平衡进行调整
     * @param key
     * @param value
     */
    public void put(int key, Object value) {
        // 1. 找空位
        Node p = root;
        Node parent = null;
        while(p != null) {
            parent = p;
            if(key < p.key) {
                p = p.left;
            } else if(p.key < key) {
                p = p.right;
            } else {
                p.value = value;  // 更新
                return;
            }
        }
        Node inserted = new Node(key, value);
        if(parent == null) {
            root = inserted;
        } else if(key < parent.key) {
            // 左孩子
            parent.left = inserted;
            inserted.parent = parent;
        } else {
            // 右孩子
            parent.right = inserted;
            inserted.parent = parent;
        }
        // 红红相邻调整
        fixRedRed(inserted);
    }
    /**
     * 红红相邻
     * @param x
     */
    private void fixRedRed(Node x) {
        if(x == root) {
            // 情况1 插入节点为根节点,将根节点变黑
            x.color = Color.BLACK;
            return;
        }
        if(isBlack(x.parent)) {
            // 情况2 插入节点的父节点为黑色,树的红黑特性不变,无需调整
            return;
        }
        Node parent = x.parent;
        Node uncle = x.uncle();
        Node grandparent = parent.parent;
        if(isRed(uncle)) {
            // 情况3 插入节点的父节点与叔叔节点为红色
            // 父亲变为黑色,为了保证黑色平衡,连带的叔叔也变为黑色
            parent.color = Color.BLACK;
            uncle.color = Color.BLACK;
            // 祖父如果是黑色不变,会造成这棵子树黑色过多,因此祖父节点变为红色
            grandparent.color = Color.RED;
            // 如果祖父变成红色,可能会触发红红相邻,因此对祖父进行递归调整
            fixRedRed(grandparent);
            return;
        }
        // 情况4 插入节点的父节点为红色,叔叔为黑色
        // 1. 父亲为左孩子,插入节点也是左孩子,此时即LL不平衡 -> 右旋
        if(parent.isLeftChild() && x.isLeftChild()) {
            // 父亲变黑
            parent.color = Color.BLACK;
            // 祖父变红
            grandparent.color = Color.RED;
            // 右旋
            rightRotate(grandparent);
        }
        // 2. 父亲为左孩子,插入节点是右孩子,此时即LR不平衡 -> 左右旋
        else if(parent.isLeftChild()) {
            // 以父节点为支点进行左旋
            leftRotate(parent);
            // 插入节点变黑
            x.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行右旋
            rightRotate(grandparent);
        }
        // 3. 父亲为右孩子,插入节点也是右孩子,此时即RR不平衡 -> 左旋
        else if(!x.isLeftChild()) {
            // 父节点变黑
            parent.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行左旋
            leftRotate(grandparent);
        }
        // 4. 父亲节点为右孩子,插入节点是左孩子,此时即RL不平衡 -> 右左旋
        else {
            // 以父节点为支点进行右旋
            rightRotate(parent);
            // 插入节点变黑
            x.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行左旋
            leftRotate(grandparent);
        }
    }CASE 4-1:LL
调整前:

调整后:
- 父亲变黑

- 祖父变红

- 右旋

CASE 4-2:LR
调整前:

调整后:
- 左旋

- 父节点变黑色,祖父节点变红色

- 右旋

2. 删除情况
case 0:如果删除节点有两个孩子
- 交换删除节点和后继节点的key,value,递归删除后继节点,直到该节点没有孩子或只剩下一个孩子
如果删除节点没有孩子或只剩一个孩子
case 1:删的是根节点
- 删完了,直接将root = null
- 用剩余节点替换根节点的key,value,根节点孩子null,颜色保持黑色⚫️不变
删黑色⚫️会失衡,删红色不会失衡,但删黑色有一种简单情况
case 2:删的是黑⚫️,剩下的是红🔴,剩下这个红节点变黑⚫️
删除节点和剩余节点都是⚫️黑,触发双黑,双黑的意思是,少了一个黑
case 3:被调整节点的兄弟为红🔴,此时两个侄子定为黑⚫️
- 删除节点是左孩子,父亲左旋
- 删除节点是右孩子,父亲右旋
- 父亲和兄弟要变色,保证旋转后颜色平衡
- 旋转的目的是让黑侄子变为删除节点的黑兄弟,对删除节点再次递归,进入case 4 或 case 5
case 4:被调整节点的兄弟为黑⚫️,两个侄子都为黑⚫️
- 将兄弟变红🔴,目的是将删除节点和兄弟那边的黑色高度同时减少1
- 如果父亲是红🔴,则需将父亲变为黑⚫️,避免红红,此时路径黑节点数目不变
- 如果父亲是黑⚫️,说明这条路径还是少黑,再次让父节点触发双黑
case 5:被调整节点的兄弟为黑,至少一个红🔴侄子
①如果兄弟是左孩子,左侄子是红,LL不平衡
- 将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️,平衡起见,左侄子也是黑⚫️
- 原来兄弟要成为父亲,需要保留父亲颜色
例如,删除节点4

右旋

变色

②如果兄弟是左孩子,右侄子是红🔴,LR不平衡
- 将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️
- 右侄子会取代原来父亲,因此它保留父亲颜色
- 兄弟已经是黑了⚫️,无需改变
例如,要删除节点4

左旋(以兄弟为支点)

右旋(以父节点为支点)

变色

③如果兄弟是右孩子,右侄子是红🔴,RR不平衡
- 将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️,平衡起见,右侄子也是黑⚫️
- 原来兄弟要成为父亲,需要保留父亲颜色
死如果兄弟是右孩子,左侄子是红🔴,RL不平衡
- 将来删除节点这边少个黑,所以最后旋转过来的父亲需要变成黑⚫️
- 左侄子会取代原来父亲,因此它保留父亲颜色
- 兄弟已经是黑了⚫️,无需改变
    /**
     * 解决双黑问题 -> case 3、 case 4、 case 5
     * @param x
     */
    private void fixDoubleBlack(Node x) {
        if(x == root) {
            return;
        }
        Node parent = x.parent;
        Node sibling = x.sibling();
        // case 3:被调整节点的兄弟为红色,此时两个侄子定为黑色
        if(isRed(sibling)) {
            if(x.isLeftChild()) {
                // 删除节点是左孩子 -> 父亲左旋
                leftRotate(parent);
            } else {
                // 删除节点是右孩子 -> 父亲右旋
                rightRotate(parent);
            }
            // 父亲和兄弟要变色,保证旋转后颜色平衡
            parent.color = Color.RED;
            sibling.color = Color.BLACK;
            // 旋转的目的是让黑侄子变为删除节点的黑兄弟,对删除节点再次递归,进入case 4或 case 5
            fixDoubleBlack(x);
            return;
        }
        if (sibling != null) {
            // case 4:被调整节点的兄弟为黑,两个侄子都为黑
            if(isBlack(sibling.left) && isBlack(sibling.right)) {
                // 1. 将兄弟变红,目的是将删除节点和兄弟那边的黑色高度同时减少1
                sibling.color = Color.RED;
                if(isRed(parent)) {
                    // 2. 如果父亲是红,则需将父亲变为黑,避免红红,此时路径黑节点数目不变
                    parent.color = Color.BLACK;
                } else {
                    // 3. 如果父亲是黑,说明这条路径还是少黑,再次让父节点触发双黑
                    fixDoubleBlack(parent);
                }
            }
            // case 5:被调整节点的兄弟是黑色,至少一个侄子是红色
            else {
                // 5.1 如果兄弟是左孩子,左侄子是红色,LL不平衡
                if(sibling.isLeftChild() && isRed(sibling.left)) {
                    // 右旋
                    rightRotate(parent);
                    // 左侄子变黑色
                    sibling.left.color = Color.BLACK;
                    // 原来兄弟要成为父亲,需要保留父亲颜色
                    sibling.color = parent.color;
                }
                // 5.2 如果兄弟是左孩子,右侄子是红色,LR不平衡
                else if(sibling.isLeftChild() && isRed(sibling.right)) {
                    // 右侄子会取代原来的父亲,保留父亲的颜色
                    sibling.right.color = parent.color;
                    // 左旋(以兄弟为支点)
                    leftRotate(sibling);
                    // 右旋(以父节点为支点)
                    rightRotate(parent);
                }
                // 5.3 如果兄弟是右孩子,右侄子是红色,RR不平衡
                else if(!sibling.isLeftChild() && isRed(sibling.right)) {
                    // 左旋
                    leftRotate(parent);
                    // 右侄子变成黑色
                    sibling.right.color = Color.BLACK;
                    // 原来兄弟要成为父亲,需要保留父亲颜色
                    sibling.color = parent.color;
                }
                // 5.4 如果兄弟是右孩子,左侄子是红色,RL不平衡
                else {
                    // 左侄子会取代原来父亲,因此它保留父亲颜色
                    sibling.left.color = parent.color;
                    // 右旋(以兄弟为支点)
                    rightRotate(sibling);
                    // 左旋(以父亲为支点)
                    leftRotate(parent);
                }
                // 旋转过来的父亲要变成黑色
                parent.color = Color.BLACK;
            }
        } else {
            // @TODO 实际也不会出现,触发双黑后,兄弟节点不会为 null
            fixDoubleBlack(parent);
        }
    }
    private void doRemove(Node deleted) {
        Node replaced = findReplaced(deleted);
        Node parent = deleted.parent;
        // 1. 没有孩子
        if(replaced == null) {
            // case 1:删除的是根节点,且删完了,直接将root = null
            if(deleted == root) {
                root = null;
            } else {
                // 删除的是叶子节点
                if(isBlack(deleted)) {
                    // 删除节点和剩余节点(空节点)都是黑色,复杂调整
                    fixDoubleBlack(deleted);
                } else {
                    // 删除节点是红色,无需任何处理
                }
                if(deleted.isLeftChild()) {
                    // 被删除节点是父亲的左孩子
                    parent.left = null;
                } else {
                    // 被删除节点是父亲的右孩子
                    parent.right = null;
                }
                deleted.parent = null;  // help GC
            }
            return;
        }
        // 2. 有一个孩子
        if(deleted.left == null || deleted.right == null) {
            // case 1: 删除的是根节点,用剩余节点替换根节点key、value,根节点孩子=null,颜色保持黑色不变
            if(deleted == root) {
                // 孩子节点不会再有孩子,否则树高不平衡
                root.key = replaced.key;
                root.value = replaced.value;
                root.left = root.right = null;
            } else {
                // 删除的是非叶子节点 -> 先删除,再调整
                if(deleted.isLeftChild()) {
                    // 删除的是父节点的左孩子
                    parent.left = replaced;
                } else {
                    // 删除的是父节点的右孩子
                    parent.right = replaced;
                }
                replaced.parent = parent;
                deleted.left = deleted.right = deleted.parent = null; // help GC
                // 删除节点和剩下节点都是黑,触发双黑 -> 少了一个黑
                if(isBlack(deleted) && isBlack(replaced)) {
                    // 复杂处理
                    fixDoubleBlack(replaced);
                } else {
                    // case 2:删的是黑色,剩下的是红色,剩下这个红节点变黑
                    replaced.color = Color.BLACK;
                }
            }
            return;
        }
        // 3. case 0:被删除节点有两个孩子 => 有一个孩子 或 没有孩子
        // 3.1 交换被删节点和后继节点的key,value值
        int k = deleted.key;
        deleted.key = replaced.key;
        replaced.key = k;
        Object v = deleted.value;
        deleted.value = replaced.value;
        replaced.value = v;
        // 3.2 递归删除后继节点,直到该节点没有孩子或只剩一个孩子
        doRemove(replaced);
    }3. 完整代码
package com.itheima.datastructure.RedBlackTree;
import com.sun.org.apache.regexp.internal.RE;
/**
 * 红黑树
 */
public class RedBlackTree {
    enum Color {
        RED, BLACK;
    }
    private static class Node {
        int key;
        Object value;
        Node left;
        Node right;
        Node parent;
        Color color = Color.RED;  // 颜色
        public Node(int key) {
            this.key = key;
        }
        public Node(int key, Object value) {
            this.key = key;
            this.value = value;
        }
        public Node(int key, Color color) {
            this.key = key;
            this.color = color;
        }
        public Node(int key, Node left, Node right, Color color) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.color = color;
            if(left != null) {
                left.parent = this;
            }
            if(right != null) {
                right.parent = this;
            }
        }
        /**
         * 判断是否是左孩子
         * @return
         */
        public boolean isLeftChild() {
            return parent != null && parent.left == this;
        }
        /**
         * 找叔叔节点
         * @return
         */
        public Node uncle() {
            if(parent == null || parent.parent == null) {
                return null;
            }
            if(parent.isLeftChild()) {
                return parent.parent.right;
            } else {
                return parent.parent.left;
            }
        }
        /**
         * 找兄弟节点
         * @return
         */
        public Node sibling() {
            if(parent == null) {  // 根节点
                return null;
            }
            if(this.isLeftChild()) {
                return parent.right;
            } else {
                return parent.left;
            }
        }
    }
    private Node root;
    /**
     * 判断节点是否是红色
     * @param node
     * @return
     */
    public boolean isRed(Node node) {
        return node != null && node.color == Color.RED;
    }
    /**
     * 判断节点是否为黑色
     * @param node
     * @return
     */
    public boolean isBlack(Node node) {
        return node == null || node.color == Color.BLACK;
    }
    /**
     * 右旋
     * 1. parent的处理
     * 2. 旋转后新根的父子关系
     * @param pink
     */
    private void rightRotate(Node pink) {
        Node parent = pink.parent;
        Node yellow = pink.left;
        Node green = yellow.right;
        // 被旋转节点的右子树不为空
        if(green != null) {
            green.parent = pink;
        }
        yellow.right = pink;
        yellow.parent = parent;
        pink.left = green;
        pink.parent = yellow;
        if(parent == null) {
            // 旋转后yellow为根节点
            root = yellow;
        }else if(parent.left == pink) {
            // 左子树
            parent.left = yellow;
        } else {
            // 右子树
            parent.right = yellow;
        }
    }
    /**
     * 左旋
     * @param pink
     */
    private void leftRotate(Node pink) {
        Node parent = pink.parent;
        Node yellow = pink.right;
        Node green = yellow.left;
        if(green != null) {
            green.parent = pink;
        }
        yellow.left = pink;
        yellow.parent = parent;
        pink.right = green;
        pink.parent = yellow;
        if(parent == null) {
            root = yellow;
        } else if(parent.left == pink) {
            parent.left = yellow;
        } else {
            parent.right = yellow;
        }
    }
    /**
     * 新增或更新
     * 正常增、遇到红红不平衡进行调整
     * @param key
     * @param value
     */
    public void put(int key, Object value) {
        // 1. 找空位
        Node p = root;
        Node parent = null;
        while(p != null) {
            parent = p;
            if(key < p.key) {
                p = p.left;
            } else if(p.key < key) {
                p = p.right;
            } else {
                p.value = value;  // 更新
                return;
            }
        }
        Node inserted = new Node(key, value);
        if(parent == null) {
            root = inserted;
        } else if(key < parent.key) {
            // 左孩子
            parent.left = inserted;
            inserted.parent = parent;
        } else {
            // 右孩子
            parent.right = inserted;
            inserted.parent = parent;
        }
        // 红红相邻调整
        fixRedRed(inserted);
    }
    /**
     * 红红相邻
     * @param x
     */
    private void fixRedRed(Node x) {
        if(x == root) {
            // 情况1 插入节点为根节点,将根节点变黑
            x.color = Color.BLACK;
            return;
        }
        if(isBlack(x.parent)) {
            // 情况2 插入节点的父节点为黑色,树的红黑特性不变,无需调整
            return;
        }
        Node parent = x.parent;
        Node uncle = x.uncle();
        Node grandparent = parent.parent;
        if(isRed(uncle)) {
            // 情况3 插入节点的父节点与叔叔节点为红色
            // 父亲变为黑色,为了保证黑色平衡,连带的叔叔也变为黑色
            parent.color = Color.BLACK;
            uncle.color = Color.BLACK;
            // 祖父如果是黑色不变,会造成这棵子树黑色过多,因此祖父节点变为红色
            grandparent.color = Color.RED;
            // 如果祖父变成红色,可能会触发红红相邻,因此对祖父进行递归调整
            fixRedRed(grandparent);
            return;
        }
        // 情况4 插入节点的父节点为红色,叔叔为黑色
        // 1. 父亲为左孩子,插入节点也是左孩子,此时即LL不平衡 -> 右旋
        if(parent.isLeftChild() && x.isLeftChild()) {
            // 父亲变黑
            parent.color = Color.BLACK;
            // 祖父变红
            grandparent.color = Color.RED;
            // 右旋
            rightRotate(grandparent);
        }
        // 2. 父亲为左孩子,插入节点是右孩子,此时即LR不平衡 -> 左右旋
        else if(parent.isLeftChild()) {
            // 以父节点为支点进行左旋
            leftRotate(parent);
            // 插入节点变黑
            x.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行右旋
            rightRotate(grandparent);
        }
        // 3. 父亲为右孩子,插入节点也是右孩子,此时即RR不平衡 -> 左旋
        else if(!x.isLeftChild()) {
            // 父节点变黑
            parent.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行左旋
            leftRotate(grandparent);
        }
        // 4. 父亲节点为右孩子,插入节点是左孩子,此时即RL不平衡 -> 右左旋
        else {
            // 以父节点为支点进行右旋
            rightRotate(parent);
            // 插入节点变黑
            x.color = Color.BLACK;
            // 祖父节点变红
            grandparent.color = Color.RED;
            // 以祖父节点为支点进行左旋
            leftRotate(grandparent);
        }
    }
    /**
     * 查找删除节点
     * @param key
     * @return
     */
    private Node find(int key) {
        Node p = root;
        while(p != null) {
            if(key < p.key) {
                p = p.left;
            } else if(p.key < key) {
                p = p.right;
            } else {
                return p;
            }
        }
        return null;
    }
    /**
     * 查找被删除节点的剩余节点
     * @param deleted
     * @return
     */
    private Node findReplaced(Node deleted) {
        // 1. 被删除节点是叶子节点
        if(deleted.left == null && deleted.right == null) {
            return null;
        }
        // 2. 只有右孩子
        if(deleted.left == null) {
            return deleted.right;
        }
        // 3. 只有左孩子
        if(deleted.right == null) {
            return deleted.left;
        }
        // 4. 有两个孩子 -> 找后继
        Node s = deleted.right;
        while(s.left != null) {
            s = s.left;
        }
        return s;
    }
    /**
     * 删除
     * 正常删,会用到李代桃僵技巧,遇到黑黑不平衡进行调整
     * @param key
     */
    public void remove(int key) {
        Node deleted = find(key);
        // 被删除节点不存在
        if(deleted == null) {
            return;
        }
        
        doRemove(deleted);
    }
    /**
     * 解决双黑问题 -> case 3、 case 4、 case 5
     * @param x
     */
    private void fixDoubleBlack(Node x) {
        if(x == root) {
            return;
        }
        Node parent = x.parent;
        Node sibling = x.sibling();
        // case 3:被调整节点的兄弟为红色,此时两个侄子定为黑色
        if(isRed(sibling)) {
            if(x.isLeftChild()) {
                // 删除节点是左孩子 -> 父亲左旋
                leftRotate(parent);
            } else {
                // 删除节点是右孩子 -> 父亲右旋
                rightRotate(parent);
            }
            // 父亲和兄弟要变色,保证旋转后颜色平衡
            parent.color = Color.RED;
            sibling.color = Color.BLACK;
            // 旋转的目的是让黑侄子变为删除节点的黑兄弟,对删除节点再次递归,进入case 4或 case 5
            fixDoubleBlack(x);
            return;
        }
        if (sibling != null) {
            // case 4:被调整节点的兄弟为黑,两个侄子都为黑
            if(isBlack(sibling.left) && isBlack(sibling.right)) {
                // 1. 将兄弟变红,目的是将删除节点和兄弟那边的黑色高度同时减少1
                sibling.color = Color.RED;
                if(isRed(parent)) {
                    // 2. 如果父亲是红,则需将父亲变为黑,避免红红,此时路径黑节点数目不变
                    parent.color = Color.BLACK;
                } else {
                    // 3. 如果父亲是黑,说明这条路径还是少黑,再次让父节点触发双黑
                    fixDoubleBlack(parent);
                }
            }
            // case 5:被调整节点的兄弟是黑色,至少一个侄子是红色
            else {
                // 5.1 如果兄弟是左孩子,左侄子是红色,LL不平衡
                if(sibling.isLeftChild() && isRed(sibling.left)) {
                    // 右旋
                    rightRotate(parent);
                    // 左侄子变黑色
                    sibling.left.color = Color.BLACK;
                    // 原来兄弟要成为父亲,需要保留父亲颜色
                    sibling.color = parent.color;
                }
                // 5.2 如果兄弟是左孩子,右侄子是红色,LR不平衡
                else if(sibling.isLeftChild() && isRed(sibling.right)) {
                    // 右侄子会取代原来的父亲,保留父亲的颜色
                    sibling.right.color = parent.color;
                    // 左旋(以兄弟为支点)
                    leftRotate(sibling);
                    // 右旋(以父节点为支点)
                    rightRotate(parent);
                }
                // 5.3 如果兄弟是右孩子,右侄子是红色,RR不平衡
                else if(!sibling.isLeftChild() && isRed(sibling.right)) {
                    // 左旋
                    leftRotate(parent);
                    // 右侄子变成黑色
                    sibling.right.color = Color.BLACK;
                    // 原来兄弟要成为父亲,需要保留父亲颜色
                    sibling.color = parent.color;
                }
                // 5.4 如果兄弟是右孩子,左侄子是红色,RL不平衡
                else {
                    // 左侄子会取代原来父亲,因此它保留父亲颜色
                    sibling.left.color = parent.color;
                    // 右旋(以兄弟为支点)
                    rightRotate(sibling);
                    // 左旋(以父亲为支点)
                    leftRotate(parent);
                }
                // 旋转过来的父亲要变成黑色
                parent.color = Color.BLACK;
            }
        } else {
            // @TODO 实际也不会出现,触发双黑后,兄弟节点不会为 null
            fixDoubleBlack(parent);
        }
    }
    private void doRemove(Node deleted) {
        Node replaced = findReplaced(deleted);
        Node parent = deleted.parent;
        // 1. 没有孩子
        if(replaced == null) {
            // case 1:删除的是根节点,且删完了,直接将root = null
            if(deleted == root) {
                root = null;
            } else {
                // 删除的是叶子节点
                if(isBlack(deleted)) {
                    // 删除节点和剩余节点(空节点)都是黑色,复杂调整
                    fixDoubleBlack(deleted);
                } else {
                    // 删除节点是红色,无需任何处理
                }
                if(deleted.isLeftChild()) {
                    // 被删除节点是父亲的左孩子
                    parent.left = null;
                } else {
                    // 被删除节点是父亲的右孩子
                    parent.right = null;
                }
                deleted.parent = null;  // help GC
            }
            return;
        }
        // 2. 有一个孩子
        if(deleted.left == null || deleted.right == null) {
            // case 1: 删除的是根节点,用剩余节点替换根节点key、value,根节点孩子=null,颜色保持黑色不变
            if(deleted == root) {
                // 孩子节点不会再有孩子,否则树高不平衡
                root.key = replaced.key;
                root.value = replaced.value;
                root.left = root.right = null;
            } else {
                // 删除的是非叶子节点 -> 先删除,再调整
                if(deleted.isLeftChild()) {
                    // 删除的是父节点的左孩子
                    parent.left = replaced;
                } else {
                    // 删除的是父节点的右孩子
                    parent.right = replaced;
                }
                replaced.parent = parent;
                deleted.left = deleted.right = deleted.parent = null; // help GC
                // 删除节点和剩下节点都是黑,触发双黑 -> 少了一个黑
                if(isBlack(deleted) && isBlack(replaced)) {
                    // 复杂处理
                    fixDoubleBlack(replaced);
                } else {
                    // case 2:删的是黑色,剩下的是红色,剩下这个红节点变黑
                    replaced.color = Color.BLACK;
                }
            }
            return;
        }
        // 3. case 0:被删除节点有两个孩子 => 有一个孩子 或 没有孩子
        // 3.1 交换被删节点和后继节点的key,value值
        int k = deleted.key;
        deleted.key = replaced.key;
        replaced.key = k;
        Object v = deleted.value;
        deleted.value = replaced.value;
        replaced.value = v;
        // 3.2 递归删除后继节点,直到该节点没有孩子或只剩一个孩子
        doRemove(replaced);
    }
}
4. 小结
| 维度 | 普通二叉搜索树 | AVL树 | 红黑树 | 
| 查询 | 平均O(log n),最坏O(n) | O(logn) | O(logn) | 
| 插入 | 平均O(logn),最坏O(n) | O(logn) | O(logn) | 
| 删除 | 平均O(logn),最坏O(n) | O(logn) | O(logn) | 
| 平衡性 | 不平衡 | 严格平衡 | 近似平衡 | 
| 结构 | 二叉树 | 自平衡的二叉树 | 具有红黑性质的自平衡二叉树 | 
| 查找效率 | 低 | 高 | 高 | 
| 插入删除效率 | 低 | 中等 | 高 | 
普通二叉树插入、删除、查询的时间复杂度与树的高度有关,因此在最坏情况下,时间复杂度为O(n),而且容易退化为链表,查找效率低。
AVL树是一种高度平衡的二叉搜索树,其左右子树的高度差不超过1。因此,它能够在logn的平均时间内完成插入、删除、查询操作,但是在维护平衡的过程中,需要频繁地进行旋转操作,导致插入删除效率较低。
红黑树是一种近似平衡的二叉搜索树,它在保持高度平衡的同时,又能够保持较高的插入、删除效率。红黑树通过节点着色和旋转操作来维护平衡。红黑树在维护平衡的过程中,能够进行较少的节点旋转操作,因此插入删除效率较高,并且查询效率也较高。
综上所述,红黑树具有较高的综合性能,是一种广泛应用的数据结构。



















