手撕红黑树源码(包含各种旋转示例)

一、类及内部结点的定义

java 复制代码
public class RedBlackTree<K extends Comparable<K>> {

    private Node root;
    private final static boolean RED = false;
    private final static boolean BLACK = true;

class Node {
        boolean color;
        K key;
        Node left;
        Node right;
        Node parent; //不使用递归,就需要记录父结点

        public Node(boolean color, K key, Node left, Node right, Node parent) {
            this.color = color;
            this.key = key;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }
    }
    
}

二、插入元素

一直比较,先找到要插入的位置,再插入

java 复制代码
 public void insert(K key) {
        if (key == null) throw new IllegalArgumentException("param is null");
        insert(new Node(RED, key, null, null, null));
    }

 public void insert(Node newNode) {
        Node mid = root; //遍历节点
        Node midF = null; //待插入结点父节点
        int com = 0;
        //找到待插入结点的父节点
        while (mid != null) {
            midF = mid;
            com = newNode.key.compareTo(midF.key);
            if (com < 0) mid = mid.left;
            if (com >= 0) mid = mid.right;
        }
        newNode.parent = midF;

        //判断父节点是否为空
        if (midF != null) {  //非空
            //比较新结点与父节点的大小,判断是插入到左边还是右边
            if (com < 0) midF.left = newNode;
            if (com >= 0) midF.right = newNode;
        } else {   //空,也就是root = null;
            root = newNode;
        }
        //修正
        insertFixUp(newNode);
    }

三、插入后修正

理论上插入后有五种情况:

3.1 根结点为空,直接插入作为根结点

不需要修正

3.2 插入结点的父结点为黑色,直接插入

不需要修正

3.3 插入结点的父亲结点结点为红色

需要修正

3.3.1 叔叔结点为红色

处理方法:直接变色

Exp1:"3"为新插入结点,变色

Exp2: "30"为新插入结点,变色

3.3.2 叔叔结点为黑色(nil结点)插入结点&父结点&爷爷结点在一条直线上

处理方法:插入结点的爷爷结点左旋或者右旋

Exp1: "3"为新插入结点,"10"右旋

Exp2: "3"为新插入结点,"1"左旋

3.3.3 叔叔结点为黑色(nil结点)插入结点&父结点&爷爷结点不在一条直线上(可以看作是三者构成一个三角形)

处理方法:插入结点的父亲结点左旋或者右旋,之后就回到了三者在一条直线上的情况,合并处理(上一种)

Exp1: "8"为新插入结点,父结点"5"左旋,之后回到上一中情况

Exp2: "15"为新插入结点,父结点"20"左旋,之后回到上一中情况

java 复制代码
 private void insertFixUp(Node node) {
        //新插入结点的父节点与祖父节点
        Node parent;
        Node grandParent;
        //新插入结点的父节点非空为红色时,才需要修正
        while ((parent = node.parent) != null && parent.color == RED) {
            grandParent = parent.parent;
            //如果父节点是爷爷结点左孩子
            if (grandParent.left == parent) {
                Node uncle = grandParent.right; //叔叔结点
                //case1:叔叔结点为红色 变色即可
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    grandParent.color = RED;
                    uncle.color = BLACK;
                    //继续向上调整
                    node = grandParent;
                    //跳出本次循环
                    continue;
                }
                //case2:叔叔结点为黑色,且新结点为父节点的右孩子(要对父节点左旋变成case3)
                //走到这,可能不存在叔叔结点,或者叔叔结点为黑色(nil)
                if (parent.right == node) {
                    leftRotate(parent);
                    //更新parent
                    parent = parent.parent;
                }
                //case3:叔叔结点为黑色,且新结点为父节点的左孩子(变色右旋)
                //走到这,新节点为左孩子
                parent.color = BLACK;
                parent.parent.color = RED;
                rightRotate(parent.parent);
            } else {
                //如果父节点是爷爷结点右孩子
                Node uncle = grandParent.left; //叔叔结点
                //case1:叔叔结点为红色 变色即可
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    grandParent.color = RED;
                    uncle.color = BLACK;
                    //继续向上调整
                    node = grandParent;
                    //跳出本次循环
                    continue;
                }
                //case2:叔叔结点为黑色,且新结点为父节点的左孩子(要对父节点右旋变成case3)
                if (parent.left == node) {
                    rightRotate(parent);
                    //更新parent!!!(旋转之后parent变换了位置)
                    parent = parent.parent; //parent = node(新插入的结点)
                }
                //case3:叔叔结点为黑色,且新结点为父节点的右孩子(变色左旋)
                parent.color = BLACK;
                parent.parent.color = RED;
                leftRotate(parent.parent);
            }
        }
        //新插入结点父节点为空,说明插入到了根节点
        root.color = BLACK;
    }

四、左旋/右旋

左右旋只研究一种情况即可,假如我们研究左旋。

左旋只分为两种情况:

4.1 插入结点&父结点&爷爷结点在一条直线上

Exp1:旋转结点是根结点,旋转"10"

Exp2:旋转结点不是根结点,旋转"20"

4.2 插入结点&父结点&爷爷结点不在一条直线上

Exp:旋转"5"

java 复制代码
//写这部分代码,画图就好理解了
private void leftRotate(Node h) {
        Node son = h.right;  //目标结点的右孩子

        h.right = son.left; //设置旋转之后目标结点的右孩子
        if(son.left != null) son.left.parent = h; //设置son.left的父节点

        son.parent = h.parent;  //设置son结点的父节点
        if(h.parent == null){   //若原来目标结点父节点为null(根节点),将son设为根节点
            root = son;
        }else {
            //走到这里,原来目标结点一定有父节点
            //判断h是父节点的左孩子还是右孩子,并设置其孩子
            if(h.parent.left == h){
                h.parent.left = son;
            }else {
                h.parent.right = son;
            }
        }

        //设置son的左孩子
        son.left = h;
        //最后将原来目标结点的父节点设置为son
        h.parent = son;
    }


private void rightRotate(Node h) {
        Node son = h.left;  //目标结点的左孩子

        h.left = son.right; //设置旋转之后目标结点的左孩子
        if(son.right != null) son.right.parent = h; //设置son.right的父节点

        son.parent = h.parent;  //设置son结点的父节点
        if(h.parent == null){   //若原来目标结点父节点为null(根节点),将son设为根节点
            root = son;
        }else {
            //走到这里,原来目标结点一定有父节点
            //判断h是父节点的左孩子还是右孩子,并设置其孩子
            if(h.parent.left == h){
                h.parent.left = son;
            }else {
                h.parent.right = son;
            }
        }

        //设置son的左孩子
        son.right = h;
        //最后将原来目标结点的父节点设置为son
        h.parent = son;
   }

五、删除元素

删除结点需要找到替换结点

中序遍历的删除结点的前驱(删除结点左孩子的最右结点)或者后继(删除结点右孩子的最左结点)

  • 删除元素无子结点时,删除结点可能为红色或者黑色

    • 若删除结点为红色,直接删除即可
    • 若删除结点为黑色,删除后需要修正(最麻烦部分)
  • 删除结点只有一个子结点 ,那么删除结点只能为黑色,其子结点只能为红色(否则无法满足红黑树的性质),此时使用子结点作为替换结点即可,把替换结点(删除结点的孩子结点)置为黑色即可

  • 删除结点的左右子树都不空,使用中序遍历的后继结点作为替换结点(即删除元素右子树的最左节点),之后就转化为上面两种情况

java 复制代码
public void remove(K key) {
        Node node = get(root, key);
        if (node != null) remove(node);
    }

    private void remove(Node node) {
        if (node == null) return;
        Node replaceNode;  //删除结点的代替结点

        // Case1:左右子树都不空
        if (node.left != null && node.right != null) {
            //找到代替节点(中序遍历的后继节点,即右子树的最左结点)
            replaceNode = node;  //遍历结点的父结点(最后会指向替换结点)
            Node mid = node.right; //遍历结点,遍历右子树的左节点
            while (mid != null) {
                replaceNode = mid;
                mid = mid.left;
            }
            //找到代替结点,交换值,删除代替结点
            K value = node.key;
            node.key = replaceNode.key;
            replaceNode.key = value;
            remove(replaceNode);
            return;
        }

        // Case2: 有一个孩子或者没有孩子
        //找到替换结点(左孩子或者右孩子(右孩子可能为nil))
        if (node.left != null) replaceNode = node.left;
        else replaceNode = node.right;

        //替换删除
        Node parent = node.parent; //要删除结点的父结点
        if (parent == null) {
            //如果当前父结点为空(node为根结点)
            root = replaceNode;
            //不要忘记设置替换结点的parent
            if (replaceNode != null) replaceNode.parent = null;
        } else {
            //如果当前父结点非空
            if (replaceNode != null) replaceNode.parent = parent;
            //设置删除结点父结点的子结点
            if (parent.left == node) {
                parent.left = replaceNode;
            } else {
                parent.right = replaceNode;
            }
        }
        //若删除的叶子结点为黑色,则需要进行修正
        if (node.color == BLACK) removeFix(parent, replaceNode);
    }

六、删除后修正

删除动作(移除节点)之后,看看这个节点是不是黑色的叶子节点,如果不是,简单处理就可以达到平衡了;

先看删除结点的父结点是不是根节点,是的话什么都不用管;不是的话看兄弟什么颜色:

2.1 兄弟是红色:进行旋转变色,去到兄弟为黑色那里处理

2.2 兄弟是黑色,看看兄弟子节点是不是全部都是黑。

(1)全黑的话,无论父亲结点是红是黑,可以统一处理;

(2)不全黑,看兄在的位置,兄在左的话,看兄的左子是不是红色,进行对应处理;兄在右的话,看兄的右子是不是红色,进行对应处理。

6.1 删除结点的兄弟节点为红色

6.1.1 兄弟结点在左孩子(兄红-兄左)

修正方法:父结点右旋,交换父结点和兄弟结点颜色,兄弟结点置为父结点左孩子,转到兄弟结点为黑色的情况

Exp:删除结点"30",父结点"20"右旋,变色,此情况转到了2.1.1

删除结果:(上面最后一步20不平衡)

6.1.2 兄弟结点在右孩子(兄红-兄右)

修正方法:父结点左旋,交换父结点和兄弟结点颜色,兄弟结点置为父结点右孩子,转到兄弟结点为黑色的情况

Exp:删除结点"10",父结点"20"左旋,变色,兄弟结点置为"25",此情况转到了2.1.1

删除结果:

6.2 删除结点的兄弟节点为黑色

6.2.1 删除结点的兄弟子结点全黑(两种情况可以统一处理)

修正方法:父亲结点变红,父亲结点变为新的代替节点,继续递归

6.2.1.1 父亲结点为红色(兄黑-兄子全黑-父红)

Exp:删除"5","10"和"15"交换颜色

6.2.1.2 父亲结点为黑色(兄黑-兄子全黑-父黑)

Exp: 删除结点"10","30"变红 ( 找不到其他情况???)

6.2.2 删除结点的兄弟子结点不全黑

6.2.2.1兄弟节点是左孩子,并且兄弟结点左孩子红色(右孩子黑)(兄黑-兄子不全黑-兄左,兄左子红)

修正方法:父结点右旋,之后交换父结点和兄弟结点的颜色,最后兄弟结点左孩子置黑

Exp:删除结点"15",父节点"10"右旋,变色

6.2.2.2 兄弟节点是左孩子,并且兄弟结点左孩子黑色(右孩子红)(兄黑-兄子不全黑-兄左,兄左子黑)

修正方法:兄弟结点左旋,交换兄弟结点和其右孩子的颜色,转到 2.2.1

Exp:删除结点"8",兄弟结点"3"左旋,转到2.2.1

删除结果:

6.2.2.3 兄弟节点是右孩子,并且兄弟结点右孩子红色(兄黑-兄子不全黑-兄右,兄右子红)

修正方法:父结点左旋,之后交换父结点和兄弟结点的颜色,最后兄弟结点右孩子置黑

Exp:删除结点"3",父结点"5"左旋,变色

6.2.2.4 兄弟节点是右孩子,并且兄弟结点右孩子黑色(兄黑-兄子不全黑-兄右,兄右子黑)

修正方法:兄弟结点右旋,交换兄弟结点和其左孩子的颜色,转到 2.2.3

Exp:删除结点"5",兄弟结点"15"右旋,转到2.2.3

删除结果:

七、一个添加/删除例子

注意:N为要平衡的结点,S为兄弟结点

(1)删除50,用60替换,等同于删除只有一个孩子的节点,简单处理即可:

(2)删除70:

​ (兄红兄左)情况1.1

​ ---> 平衡节点改变(兄黑-兄子不全黑-兄左,兄左子黑)情况 2.2.2

​ --->(兄黑-兄子不全黑-兄左,兄左子红)情况 2.2.1

(3)删除60:

​ (兄黑-兄子全黑)情况2.1

(4)删除10:

​ (兄黑-兄不全黑-兄右子黑)情况2.2.4

​ ---> (兄黑-兄不全黑-兄右子红) 情况2.2.3

(5)删除20:

​ (兄黑-兄子全黑)情况2.1

java 复制代码
private void removeFix(Node parent, Node replaceNode) {
        //删除结点为叶结点才会进入循环,即 replaceNode == null,
        // replaceNode != null 说明删除的结点有一个孩子,直接变成黑色即可
        while ((replaceNode == null || replaceNode.color == BLACK) && root != replaceNode) {
            //删除结点是左孩子
            if (replaceNode == parent.left) {
                //兄弟结点为父结点的右孩子
                Node brother = parent.right;
                //Case1:(兄弟红-兄右) 将父亲结点变红,兄弟节点变黑,父结点左旋
                if (brother != null && brother.color == RED) {
                    brother.color = BLACK;
                    parent.color = RED;
                    leftRotate(parent);
                    brother = parent.right;  //因为后面要交换兄弟节点和父结点颜色
                }
                //Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
                if (brother == null || (brother.left == null && brother.right == null)
					|| (brother.left != null && brother.right != null && brother.left.color == BLACK && 																			brother.right.color == BLACK)) {
                    if (brother != null) brother.color = RED;
                    replaceNode = parent;
                    parent = parent.parent;
                    continue;
                }
                //Case3:(兄黑-兄子不全黑-兄右子黑) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
                if (brother.left != null && brother.left.color == RED) {
                    brother.color = RED;
                    brother.left.color = BLACK;
                    rightRotate(brother);
                    //更新兄弟结点
                    brother = brother.parent;
                }
                //Case4:(兄黑-兄子不全黑-兄右子红) 交换父结点和兄弟结点颜色, 父节点左旋,最后兄右孩子置黑,跳出循环
                brother.color = parent.color;
                parent.color = BLACK;
                brother.right.color = BLACK;
                leftRotate(parent);
                replaceNode = root;
            } else {  //删除结点是右孩子或者为nil
                //兄弟结点为父结点的左孩子
                Node brother = parent.left;
                //Case1:(兄弟红-兄左) 交换父节点和兄弟结点颜色(父变红,兄变黑),父结点右旋
                if (brother != null && brother.color == RED) {
                    brother.color = BLACK;
                    parent.color = RED;
                    rightRotate(parent);
                    brother = parent.left;
                }
                //Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
                if (brother == null || (brother.left == null && brother.right == null)
 					|| (brother.left != null && brother.right != null && brother.left.color == BLACK && 																			brother.right.color == BLACK)) {
                    if (brother != null) brother.color = RED;
                    replaceNode = parent;
                    parent = parent.parent;
                    continue;
                }
                //Case3:(兄黑-兄子不全黑-兄左子黑(nil),右子红) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
                if (brother.right != null && brother.right.color == RED) {
                    brother.color = BLACK;
                    brother.right.color = BLACK;
                    leftRotate(brother);
                    //更新兄弟结点
                    brother = brother.parent;
                }
                //Case4:(兄黑-兄子不全黑-兄左子红,右子黑(nil)) 交换父结点和兄弟结点颜色, 父节点右旋,最后兄左孩子置黑,					跳出循环
                brother.color = parent.color;
                parent.color = BLACK;
                brother.left.color = BLACK;
                rightRotate(parent);
                replaceNode = root;
            }
        }
        //删除结点只有一个孩子,那么对替换结点改变颜色即可
        if (replaceNode != null) replaceNode.color = BLACK;
    }

八、查找结点

java 复制代码
 public Node get(K key) {
        return get(root, key);
 }

 private Node get(Node node, K key) {
        if (node == null) return null;
        int com = key.compareTo(node.key);
        if (com < 0) {
            return get(node.left, key);
        } else if (com > 0) {
            return get(node.right, key);
        } else {
            return node;
        }
    }

九、代码

java 复制代码
import lombok.Data;

/**
 * @Author: fzhaaa
 * @Date: 2022/5/25 8:44
 * @Description:
 */
@Data
public class RedBlackTree<K extends Comparable<K>> {

    private Node root;
    private final static boolean RED = false;
    private final static boolean BLACK = true;

    /**
     * 插入key
     *
     * @param key
     * @return
     */
    public void insert(K key) {
        if (key == null) throw new IllegalArgumentException("param is null");
        insert(new Node(RED, key, null, null, null));
    }

    /**
     * 新建一个结点插入到红黑树中
     *
     * @param newNode
     * @return
     */
    public void insert(Node newNode) {
        Node mid = root; //遍历节点
        Node midF = null; //待插入结点父节点
        int com = 0;
        //找到待插入结点的父节点
        while (mid != null) {
            midF = mid;
            com = newNode.key.compareTo(midF.key);
            if (com < 0) mid = mid.left;
            if (com >= 0) mid = mid.right;
        }
        newNode.parent = midF;

        //判断父节点是否为空
        if (midF != null) {  //非空
            //比较新结点与父节点的大小,判断是插入到左边还是右边
            if (com < 0) midF.left = newNode;
            if (com >= 0) midF.right = newNode;
        } else {   //空,也就是root = null;
            root = newNode;
        }
        //修正
        insertFixUp(newNode);
    }

    /**
     * 红黑树插入修正
     *
     * @param node 新插入的节点
     */
    private void insertFixUp(Node node) {
        //新插入结点的父节点与祖父节点
        Node parent;
        Node grandParent;
        //新插入结点的父节点非空为红色时,才需要修正
        while ((parent = node.parent) != null && parent.color == RED) {
            grandParent = parent.parent;
            //如果父节点是爷爷结点左孩子
            if (grandParent.left == parent) {
                Node uncle = grandParent.right; //叔叔结点
                //case1:叔叔结点为红色 变色即可
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    grandParent.color = RED;
                    uncle.color = BLACK;
                    //继续向上调整
                    node = grandParent;
                    //跳出本次循环
                    continue;
                }
                //case2:叔叔结点为黑色,且新结点为父节点的右孩子(要对父节点左旋变成case3)
                //走到这,可能不存在叔叔结点,或者叔叔结点为黑色(nil)
                if (parent.right == node) {
                    leftRotate(parent);
                    //更新parent
                    parent = parent.parent;
                }
                //case3:叔叔结点为黑色,且新结点为父节点的左孩子(变色右旋)
                //走到这,新节点为左孩子
                parent.color = BLACK;
                parent.parent.color = RED;
                rightRotate(parent.parent);
            } else {
                //如果父节点是爷爷结点右孩子
                Node uncle = grandParent.left; //叔叔结点
                //case1:叔叔结点为红色 变色即可
                if (uncle != null && uncle.color == RED) {
                    parent.color = BLACK;
                    grandParent.color = RED;
                    uncle.color = BLACK;
                    //继续向上调整
                    node = grandParent;
                    //跳出本次循环
                    continue;
                }
                //case2:叔叔结点为黑色,且新结点为父节点的左孩子(要对父节点右旋变成case3)
                if (parent.left == node) {
                    rightRotate(parent);
                    //更新parent!!!(旋转之后parent变换了位置)
                    parent = parent.parent; //parent = node(新插入的结点)
                }
                //case3:叔叔结点为黑色,且新结点为父节点的右孩子(变色左旋)
                parent.color = BLACK;
                parent.parent.color = RED;
                leftRotate(parent.parent);
            }
        }
        //新插入结点父节点为空,说明插入到了根节点
        root.color = BLACK;
    }

    /**
     * 删除元素,提供给外界使用
     *
     * @param key
     */
    public void remove(K key) {
        Node node = get(root, key);
        if (node != null) remove(node);
    }

    private void remove(Node node) {
        if (node == null) return;
        Node replaceNode;  //删除结点的代替结点

        if (node.left != null && node.right != null) {
            //找到代替节点(中序遍历的后继节点,即右子树的最左结点)
            replaceNode = node;  //遍历结点的父结点(最后会指向替换结点)
            Node mid = node.right; //遍历结点,遍历右子树的左节点
            while (mid != null) {
                replaceNode = mid;
                mid = mid.left;
            }
            //找到代替结点,交换值,删除代替结点
            K value = node.key;
            node.key = replaceNode.key;
            replaceNode.key = value;
            remove(replaceNode);
            return;
        }

        // Case2: 有一个孩子或者没有孩子
        //找到替换结点(左孩子或者右孩子(右孩子可能为nil))
        if (node.left != null) replaceNode = node.left;
        else replaceNode = node.right;

        //替换删除
        Node parent = node.parent; //要删除结点的父结点
        if (parent == null) {
            //如果当前父结点为空(node为根结点)
            root = replaceNode;
            //不要忘记设置替换结点的parent
            if (replaceNode != null) replaceNode.parent = null;
        } else {
            //如果当前父结点非空
            if (replaceNode != null) replaceNode.parent = parent;
            //设置删除结点父结点的子结点
            if (parent.left == node) {
                parent.left = replaceNode;
            } else {
                parent.right = replaceNode;
            }
        }
        //若删除的叶子结点为黑色,则需要进行修正
        if (node.color == BLACK) removeFix(parent, replaceNode);
    }

    /**
     * 删除后修正
     * 删除黑色叶子结点后发生的操作
     *
     * @param parent      替代节点的父结点
     * @param replaceNode 替代节点(删除结点)
     */
    private void removeFix(Node parent, Node replaceNode) {
        //删除结点为叶结点才会进入循环,即 replaceNode == null,
        // replaceNode != null 说明删除的结点有一个孩子,直接变成黑色即可
        while ((replaceNode == null || replaceNode.color == BLACK) && root != replaceNode) {
            //删除结点是左孩子
            if (replaceNode == parent.left) {
                //兄弟结点为父结点的右孩子
                Node brother = parent.right;
                //Case1:(兄弟红-兄右) 将父亲结点变红,兄弟节点变黑,父结点左旋
                if (brother != null && brother.color == RED) {
                    brother.color = BLACK;
                    parent.color = RED;
                    leftRotate(parent);
                    brother = parent.right;  //因为后面要交换兄弟节点和父结点颜色
                }
                //Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
                if (brother == null || (brother.left == null && brother.right == null)
                        || (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
                    if (brother != null) brother.color = RED;
                    replaceNode = parent;
                    parent = parent.parent;
                    continue;
                }
                //Case3:(兄黑-兄子不全黑-兄右子黑) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
                if (brother.left != null && brother.left.color == RED) {
                    brother.color = RED;
                    brother.left.color = BLACK;
                    rightRotate(brother);
                    //更新兄弟结点
                    brother = brother.parent;
                }
                //Case4:(兄黑-兄子不全黑-兄右子红) 交换父结点和兄弟结点颜色, 父节点左旋,最后兄右孩子置黑,跳出循环
                brother.color = parent.color;
                parent.color = BLACK;
                brother.right.color = BLACK;
                leftRotate(parent);
                replaceNode = root;
            } else {  //删除结点是右孩子或者为nil
                //兄弟结点为父结点的左孩子
                Node brother = parent.left;
                //Case1:(兄弟红-兄左) 交换父节点和兄弟结点颜色(父变红,兄变黑),父结点右旋
                if (brother != null && brother.color == RED) {
                    brother.color = BLACK;
                    parent.color = RED;
                    rightRotate(parent);
                    brother = parent.left;
                }
                //Case2:(兄弟黑-兄弟子全黑,父红父黑统一处理) 将兄弟结点变红,父节点变为新的替代节点,继续递归
                if (brother == null || (brother.left == null && brother.right == null)
                        || (brother.left != null && brother.right != null && brother.left.color == BLACK && brother.right.color == BLACK)) {
                    if (brother != null) brother.color = RED;
                    replaceNode = parent;
                    parent = parent.parent;
                    continue;
                }
                //Case3:(兄黑-兄子不全黑-兄左子黑(nil),右子红) 交换兄弟和兄弟左孩子颜色,兄弟结点右旋
                if (brother.right != null && brother.right.color == RED) {
                    brother.color = BLACK;
                    brother.right.color = BLACK;
                    leftRotate(brother);
                    //更新兄弟结点
                    brother = brother.parent;
                }
                //Case4:(兄黑-兄子不全黑-兄左子红,右子黑(nil)) 交换父结点和兄弟结点颜色, 父节点右旋,最后兄左孩子置黑,跳出循环
                brother.color = parent.color;
                parent.color = BLACK;
                brother.left.color = BLACK;
                rightRotate(parent);
                replaceNode = root;
            }
        }
        //删除结点只有一个孩子,那么对替换结点改变颜色即可
        if (replaceNode != null) replaceNode.color = BLACK;
    }

    /**
     * 左旋
     * 画图理解
     *
     * @param h 要旋转的目标结点
     */
    private void leftRotate(Node h) {
        Node son = h.right;  //目标结点的右孩子

        h.right = son.left; //设置旋转之后目标结点的右孩子
        if (son.left != null) son.left.parent = h; //设置son.left的父节点

        son.parent = h.parent;  //设置son结点的父节点
        if (h.parent == null) {   //若原来目标结点父节点为null(根节点),将son设为根节点
            root = son;
        } else {
            //走到这里,原来目标结点一定有父节点
            //判断h是父节点的左孩子还是右孩子,并设置其孩子
            if (h.parent.left == h) {
                h.parent.left = son;
            } else {
                h.parent.right = son;
            }
        }

        //设置son的左孩子
        son.left = h;
        //最后将原来目标结点的父节点设置为son
        h.parent = son;
    }

    /**
     * 右旋
     * 画图理解
     *
     * @param h 要旋转的目标结点
     */
    private void rightRotate(Node h) {
        Node son = h.left;  //目标结点的左孩子

        h.left = son.right; //设置旋转之后目标结点的左孩子
        if (son.right != null) son.right.parent = h; //设置son.right的父节点

        son.parent = h.parent;  //设置son结点的父节点
        if (h.parent == null) {   //若原来目标结点父节点为null(根节点),将son设为根节点
            root = son;
        } else {
            //走到这里,原来目标结点一定有父节点
            //判断h是父节点的左孩子还是右孩子,并设置其孩子
            if (h.parent.left == h) {
                h.parent.left = son;
            } else {
                h.parent.right = son;
            }
        }

        //设置son的左孩子
        son.right = h;
        //最后将原来目标结点的父节点设置为son
        h.parent = son;
    }

    /**
     * 查找key值对应的结点,提供给外部的方法
     *
     * @param key
     * @return
     */
    public Node get(K key) {
        return get(root, key);
    }

    /**
     * 查找红黑树中键值为key的结点,内部使用的方法
     * 递归实现
     *
     * @param node
     * @param key
     * @return
     */
    private Node get(Node node, K key) {
        if (node == null) return null;
        int com = key.compareTo(node.key);
        if (com < 0) {
            return get(node.left, key);
        } else if (com > 0) {
            return get(node.right, key);
        } else {
            return node;
        }
    }

    /**
     * 中序遍历
     *
     * @param root root
     */
    public void inOrder(Node root) {
        if (root == null) return;

        if (null != root.left) inOrder(root.left);
        System.out.println(root);
        if (null != root.right) inOrder(root.right);
    }

    @Data
    class Node {

        boolean color;
        K key;
        Node left;
        Node right;
        Node parent;

        public Node(boolean color, K key, Node left, Node right, Node parent) {
            this.color = color;
            this.key = key;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }

        @Override
        public String toString() {
            String colorText = color ? "黑色" : "红色";
            return "RBNode{" +
                    "color=" + colorText +
                    ", key=" + key +
                    ", left=" + (left == null ? "" : left.getKey()) +
                    ", right=" + (right == null ? "" : right.getKey()) +
                    ", parent=" + (parent == null ? "" : parent.getKey()) +
                    '}';
        }
    }
}
相关推荐
hn小菜鸡2 小时前
LeetCode 377.组合总和IV
数据结构·算法·leetcode
Deepoch2 小时前
Deepoc 大模型:无人机行业的智能变革引擎
人工智能·科技·算法·ai·动态规划·无人机
ai小鬼头3 小时前
百度秒搭发布:无代码编程如何让普通人轻松打造AI应用?
前端·后端·github
考虑考虑3 小时前
@FilterRegistration和@ServletRegistration注解
spring boot·后端·spring
一只叫煤球的猫3 小时前
🔥 同事混用@Transactional和TransactionTemplate被我怼了,三种事务管理到底怎么选?
java·spring boot·后端
heimeiyingwang9 天前
【深度学习加速探秘】Winograd 卷积算法:让计算效率 “飞” 起来
人工智能·深度学习·算法
你的人类朋友9 天前
(●'◡'●)从Dockerfile快速入门Docker Compose
后端
GetcharZp9 天前
「神器推荐」Rclone:轻松玩转云端存储,FTP 也能飞起来!
后端
华子w9089258599 天前
基于 SpringBoot+JSP 的医疗预约与诊断系统设计与实现
java·spring boot·后端
舒一笑9 天前
工作流会使用到Webhook是什么
后端·程序员