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

一、类及内部结点的定义

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()) +
                    '}';
        }
    }
}
相关推荐
安於宿命16 分钟前
0/1背包问题总结
c语言·c++·算法·leetcode·动态规划
NingDream81616 分钟前
面试题 04. 二维数组中的查找
数据结构·算法
拾漓17 分钟前
算法整理——【贪心算法练习(2)】
算法·贪心算法
空青72620 分钟前
ChatGPT在Java后端开发中的应用与影响
java·开发语言·人工智能·后端·神经网络·机器学习·chatgpt
冯宝宝^31 分钟前
图书管理系统
服务器·数据库·vue.js·spring boot·后端
西邮彭于晏38 分钟前
差分进化算法
windows·python·算法
titan TV man44 分钟前
上海市计算机学会竞赛平台2024年6月月赛丙组超级奇数
算法·数学建模·动态规划
刘钢筋universe1 小时前
leetcode hot100
java·算法·leetcode
java6666688881 小时前
深入理解Spring Boot中的容器与依赖注入
java·spring boot·后端
u0104058361 小时前
Spring Boot中的依赖注入和控制反转
java·spring boot·后端