TreeMap 红黑树 学习

二叉树介绍

学习红黑树之前怎么能少二叉树呢,二叉树作为树型数据结构的入门,是学习任何高级树的基础。
听如其名,二叉树 应该是比较容易想到他的形状的。一个根节点最多有两个字节点
二叉树
既然已经能绘制出来结构了,那怎么用java来描绘呢,连线就是可以看作对象之间的引用关系。所以节点应就能比较容易的用java来描述了。

public class Node<V> {
    private V v;
    private Node left;
    private Node right;
}

既然说到了二叉树就不得不提,二叉树的遍历方式了,即前中后三种遍历方式。
根左右、左根右、左右根
在这里插入图片描述
其实这条线也就是二叉树遍历的递归方式(红色的线对于每个节点都有一个前中后的过程,也就是代表访问节点的时机再递归过程中有三个地方)。
前序(根左右):2 1 7 9 3 5
中序(左根右):7 1 9 2 5 3
后续 (左右根):7 9 1 5 3 1

二叉排序树

二叉排序树就是说一个二叉树的节点左子树小于根节点,根节点又小于右子树
既然是 左《 中 《 右, 所以我们按照中序遍历二叉排序树,就可以得到一个从小到大的排序结果。

2、1、7、3、4、8、9、10
对这个序列进行二叉树的排序结果:
在这里插入图片描述
从二叉排序树:插入就是与当前节点比较小就往左走,大就往右走,直到为null。生成节点。 比如我现在要加入一个3.5的节点。 插入顺序为:与根节点2比较大于,走到7节点,与7比较小于,往左走到3节点,与3比较大于,走到4,与4比较小于往左走,发现已经为空了,所以就把3.5 当作4的左孩子。

二叉排序树的数据结构

public class Node<V extends Comparable<V>> {
    private V v;
    private Node left;
    private Node right;
}

注意:这里因为设计到比较所以借助了java的排序。

思考:有没有觉得上面的树生成的很不对称。树的高度太高了,如果插入 1, 2 ,3 , 4, 5, 6 这样的树就会一致往右边连接,导致二叉树退化成链表了,也就失去了二叉排序树强大的搜索能力,平衡的二叉排序树的搜索 类似二分查找,效率极高
所以我们需要一种手段把树尽量的平衡, 即AVL树,任何一个节点的左右子树的高度差不超过1。

AVL树

绝对平衡的二叉树, 就是任何节点的左右子树的高度差为0, 如果我就插入1, 2
使用二叉树是无法做到绝对平衡的。1、2,所以AVL是追求相对平衡的。任何一个节点的左右子树的高度差不超过1。

演示: 插入 1 , 2 , 3

在这里插入图片描述
现在已经不是AVL树了, 1节点的左右子树差为2, 需要进行旋转
在这里插入图片描述
AVL树的缺点:旋转次数过多,影响效率。。我理解也就是说白了还是过于平衡,

红黑树性质

所以为了解决AVL过于平衡导致旋转次数过多影响了效率问题,引入了红黑树,红黑树再旋转次数和平衡之间找到一个折衷点,既不会导致子树高度差过高,也不会导致旋转次数影响太多的效率。

2-3-4 树 (4阶的B树)

想要看多少阶的B树生长过程,可以去国外教育网址看,自己收到插入节点。
国外B树演示

234树也就是一组最多3个数,如果组内数量达到4的时候就会进行分裂
比如:插入 1, 2 , 3 , 4, 5, 6, 7, 8

先插入1、2、3
ok现在已经到达了最多的组内为3个节点,继续加入4 要进行分裂:
在这里插入图片描述
最终效果:
在这里插入图片描述
思考:呃B树怎么说呢?和二叉树不是一种,B树是从底层往上分裂,而且B树是一颗满树, 满树就是说要么没有子点,要么就全有,而且B树的数据结构真的不好表示,因为有一个组的概念。要么使用数组要么使用链表,以及组内有2个数据,那么就有三个指针指向下面的三个组。。,真的不知道怎么实现

呃。好家伙 4阶B树 怎么就和红黑树扯上关系了呢…,
我的乱几把理解来了:B树都是满树,高度差为0,4阶B树组内最多有3个节点存在,三个节点啊,三个节点是不是和一个二叉树联系起来了,一个根节点左右个挂一个孩子。。。。,如果我们使用二叉树把4阶B树表示出来,这颗二叉树不就是一个可以说从某个角度来说是具有4阶B树的高度差为0的性质,也就是绝对平衡,问题是怎么表示

在这里插入图片描述
有没有什么惊奇的发现。。。,二叉树再通过某种思维转变之后也可以变成4阶B树。
4阶B树的高度差为0,也就是对于二叉树的图中黑色节点的高度差为0,黑色绝对平衡。。。。,

如何把这个二叉树还原为4阶B树呢》》》
在这里插入图片描述
所以我们可以把红色点与其父节点看作是同一个阻的,也就是4阶B树的一个组。

4阶B树与红黑树的对应关系

摊牌了,上面的描述的就是红黑树。可以看出红黑树和4阶B树本质上是一样的。红黑树就是4阶B树的一种二叉树的思维逻辑表示。

🆗,4阶段B树 组内节点数量可以是1个、2个、3个,那么怎么对应红黑树呢?、
在这里插入图片描述
可以看出2个数量的组内节点对应的红黑树的表示是有2中,所以说4阶B树对应的红黑树可以至少有2颗, 每个红黑树只能表示一个4阶B树。

组内4节点裂变对应红黑树节点关系:
呃,这是比较难的关系,4阶B树组内4节点是不存在的,组内3节点如果加入又来了一个节点,这时候就会把组内3节点的中间节点往上层裂变出去,然后把新来的节点加入自己,既然裂变出去了,而且裂变出去找个节点是黑色节点, 裂变出去的黑色节点是要去找上层的组进行合并的,所以找个黑色节点要变成红色, 再回来看当前组,已经没有黑色节点了,所以要再3个节点中找一个中间节点作为黑色,另外两个为红色。

在这里插入图片描述
思考:如果找个裂变出去的2节点,到的上层新的组已经是3节点了,所以还要上层组还要接着裂变。。。。

现在能推到出红黑树的性质了吗?? 哈哈哈
1、红黑树的黑色节点是绝对平衡的
2、所有的叶子节点都是黑色(这里把null, 也视为黑色节点)
3、红色节点的孩子一定是黑色节点(这里把null, 也视为黑色节点)
4、节点是红色或者黑色
5、根节点是黑色

红黑树的操作

节点的数据结构

public class RBTree<K extends Comparable<K>, V> {
    /*红色*/
    private static final boolean RED = false;
    /*黑色*/
    private static final boolean BLACK = true;
    
    /*根节点*/
    private Node root;
    
    /*节点的数据结构*/
    private class Node{
        /*颜色 默认是红色*/
        private boolean color = RED;
        private K key;
        private V value;
        private Node parent;
        private Node left;
        private Node right;
        }
  }

右旋转和左旋转

在这里插入图片描述

代码实现

/**
     * 围绕p节点进行左旋转
     * @author puhg_sinosoft
     * @date 2022/8/6 16:31
     * @param p 
     */
    private void leftRotate(Node p){
        if (p != null){
            Node r = p.right;/*p的右孩子*/
            Node pP = p.parent;
            if (r != null){ /*右树都没有不需要进行左旋*/
                p.parent = r; 
                p.left = r.left;
                if (r.left !=null){
                    r.left.parent = p;
                }
                r.left = p;
                if (p == root){/*如果旋转的是根节点*/
                    r.parent = null;
                    root = r;
                }else if (pP.left == p){
                    pP.left = r;
                }else {
                    pP.right = r;
                }
            }
        }
    }

    /**
     * 围绕p节点进行右旋转
     * @author puhg_sinosoft
     * @date 2022/8/6 16:31
     * @param p
     */
    private void rightRotate(Node p){
        if (p != null){
            Node l = p.left;/*p的左孩子*/
            Node pP = p.parent;
            if (l != null){ /*左树都没有不需要进行左旋*/
                p.parent = l;
                p.left = l.right;
                if (l.right !=null){
                    l.right.parent = p;
                }
                l.right = p;
                if (p == root){/*如果旋转的是根节点*/
                    l.parent = null;
                    root = l;
                }else if (pP.left == p){
                    pP.left = l;
                }else {
                    pP.right = l;
                }
            }
        }
    }

需要注意一点就是:旋转节点是父节点是不是根节点、如果不是根几点,旋转节点是属于左孩子还是右孩子

插入操作

红黑树的插入也就是4阶B树的插入,B树的插入都是从最底层的组开始插入。如果组内数量超过3就进行往上分裂。。既然从底层组插入情况分为一下几种:
在这里插入图片描述

在这里插入图片描述
二节点的左孩子的情况就不画了,就和右孩子思路都是一样

在这里插入图片描述
总结一下:不管原先组内有几个节点,插入操作之后需要调整的必须父亲节点的颜色为红色, 既然父亲节点为了红色,那么说明至少组内得有2个节点,从图中可以很明细的看出3节点的调整和2节点的调整是很不同的,所以需要进行区分,即通过是否具有叔叔节点来区分。 对于2节点需要特别注意的是调整需要2步的情况,我们可以先把它旋转成一条线变,就可以复用2节点只需要调整一次的代码

put操作
/*插入操作*/
    public void put(K key , V value){
        if (key == null){
            throw new NullPointerException();
        }
        if (root == null){ //根节点还没有
            root = new Node(key, value, null);
            root.color = BLACK; //根节点的颜色为黑色
            return;
        }
        //寻找插入位置 即要去到最底层
        Node tp = null; //记录temp的前驱
        Node temp = root;
        int cmp = 0;
        while (temp != null){
            tp = temp;
            cmp = temp.key.compareTo(key);
            //比较
            if (cmp > 0){ 
                //key小 向左
                temp = temp.left;
            }else if (cmp < 0){
                temp = temp.right;
            }else {//注意这里等于就直接替换了
                temp.value = value;
                return;
            }
        }
        
        //创建节点
        Node node = new Node(key, value ,tp);
        
        //放在左边还是右边
        if (cmp > 0){
            tp.left = node;
        }else {
            tp.right = node;     
        }
        //调整包括位置颜色等的变化。。。
        fixAfterPut(node);
        
    }
fixAfterPut
/*调整*/
    private void fixAfterPut(Node node) {
        node.color = RED; //默认为红
        if (node !=null //不为空
                && node != root //node为根节点不用调整
                && node.parent.color == RED){ //父节点为黑不用调整
            if (node.parent == node.parent.parent.left){ //如果node的父亲是 爷爷节点的左孩子
                //是不是3组的节点 即需要判断叔叔节点存不存
                Node npr = node.parent.right;
                if (colorOf(npr) == RED){ //节点存在。 
                    // 为啥判断的是RED, 不是null? 同一个组,如果组内有三个节点,必定是中间黑,两边红
                    //只需要调整颜色就可以 即爷爷节点变成红色,父亲和叔叔节点变成黑色
                    npr.parent.color = RED; 
                    npr.color = BLACK;
                    node.parent.color = BLACK;
                }else { //说明组内是有2个节点 ---2个节点需要注意调整情况先要判断是不是一条线,不是需要先调整为一条线
                    if (node.parent.right == node){ //说明不在一条线上,需要进行调整
                         //进行一次左旋
                        leftRotate(node.parent);
                    }
                    //爷爷变红 , 父亲变黑
                    node.parent.parent.color = RED;
                    node.parent.color = BLACK;
                    //右旋 --- 左成一条线
                    rightRotate(node.parent.parent);
                }
            }else { //父亲节点是爷爷节点的右孩子
                //是不是3组的节点 即需要判断叔叔节点存不存
                Node npl = node.parent.left;
                if (colorOf(npl) == RED){ //节点存在。 
                    // 为啥判断的是RED, 不是null? 同一个组,如果组内有三个节点,必定是中间黑,两边红
                    //只需要调整颜色就可以 即爷爷节点变成红色,父亲和叔叔节点变成黑色
                    npl.parent.color = RED;
                    npl.color = BLACK;
                    node.parent.color = BLACK;
                }else { //说明组内是有2个节点 ---2个节点需要注意调整情况先要判断是不是一条线,不是需要先调整为一条线
                    if (node.parent.left == node){ //说明不在一条线上,需要进行调整
                        //进行一次右旋
                        rightRotate(node.parent);
                    }
                    //爷爷变红 , 父亲变黑
                    node.parent.parent.color = RED;
                    node.parent.color = BLACK;
                    //左旋 --- 左成一条线
                    leftRotate(node.parent.parent);
                }
            }
        }
    }

注意:四节点裂变关系裂变出去的节点颜色变成了红色,找个裂变出去的节点是跑去上层的组,所以还需要继续调整。。。,也就是说我裂变出去的节点还需要继续调增。示意图如下:
在这里插入图片描述
可以看见1节点插入之后, 调整1节点,接着调整爷爷节点。。。。。

代码调整如下:

正确的 fixAfterPut

 /*调整*/
    private void fixAfterPut(Node node) {
        node.color = RED; //默认为红
        while (node !=null //不为空
                && node != root //node为根节点不用调整
                && node.parent.color == RED){ //父节点为黑不用调整
            if (node.parent == node.parent.parent.left){ //如果node的父亲是 爷爷节点的左孩子
                //是不是3组的节点 即需要判断叔叔节点存不存
                Node npr = node.parent.parent.right;
                if (colorOf(npr) == RED){ //节点存在。 
                    // 为啥判断的是RED, 不是null? 同一个组,如果组内有三个节点,必定是中间黑,两边红
                    //只需要调整颜色就可以 即爷爷节点变成红色,父亲和叔叔节点变成黑色
                    npr.parent.color = RED; 
                    npr.color = BLACK;
                    node.parent.color = BLACK;
                    node = node.parent.parent; //分裂出去的节点继续调整
                }else { //说明组内是有2个节点 ---2个节点需要注意调整情况先要判断是不是一条线,不是需要先调整为一条线
                    if (node.parent.right == node){ //说明不在一条线上,需要进行调整
                         //进行一次左旋
                        leftRotate(node.parent);
                    }
                    //爷爷变红 , 父亲变黑
                    node.parent.parent.color = RED;
                    node.parent.color = BLACK;
                    //右旋 --- 左成一条线
                    rightRotate(node.parent.parent);
                }
            }else { //父亲节点是爷爷节点的右孩子
                //是不是3组的节点 即需要判断叔叔节点存不存
                Node npl = node.parent.parent.left;
                if (colorOf(npl) == RED){ //节点存在。 
                    // 为啥判断的是RED, 不是null? 同一个组,如果组内有三个节点,必定是中间黑,两边红
                    //只需要调整颜色就可以 即爷爷节点变成红色,父亲和叔叔节点变成黑色
                    npl.parent.color = RED;
                    npl.color = BLACK;
                    node.parent.color = BLACK;
                    node = node.parent.parent; //分裂出去的节点继续调整
                }else { //说明组内是有2个节点 ---2个节点需要注意调整情况先要判断是不是一条线,不是需要先调整为一条线
                    if (node.parent.left == node){ //说明不在一条线上,需要进行调整
                        //进行一次右旋
                        rightRotate(node.parent);
                    }
                    //爷爷变红 , 父亲变黑
                    node.parent.parent.color = RED;
                    node.parent.color = BLACK;
                    //左旋 --- 左成一条线
                    leftRotate(node.parent.parent);
                }
            }
        }
        root.color = BLACK; //根节点变黑
    }

删除操作

二叉树的删除操作

在这里插入图片描述
如果所示一颗二叉树
删除节点找个节点有哪几种情况:
1、左右孩子都没有,2、只有一个孩子(左或者右),3、左右孩子都有
那删除逻辑分别是什么呢?
🆗,
删除左右孩子都没有的节点:
比如-2, 那直接删除就行。

删除只有一个孩子节点:
比如1, 直接把删除节点的父亲节点的指向删除节点的孩子,这里说得有点简单了,具体还是得区分删除节点是父亲节点的左孩子还是右孩子。。

删除左右孩子都有的节点:
比如6, 对于这种情况的删除我们可以有2种做法,要么找左子树的最大值来替换6,图中就是5来替换6,要么找右子树的最小值来替换6,图中就是7来替换6
思考:替换如何做比较好,推荐做法是直接把值覆盖5, 然后把左字数的最大值或者右子树的最小值删掉。这样就可以避免复杂的指针移动

找到删除节点的中序中节点的前驱 findDeleteNodePre

即:中序遍历结果的前驱。
如果左子树不为空,直接往左子树的右边找就行,如果左子树为空,需要找到父辈是往右边的,因为往右边代表自己大于父辈

 /*找到中序中node节点的前驱*/
    private Node findDeleteNodePre(Node node) {
        if (node == null) {
            return null;
        }else if (node.left != null){
            Node pre = node.left;
            while (pre.right != null) {
                pre = pre.right;
            }
            return pre;
        }else {/*这种情况比较特殊,需要多加思考 如果左子树不为空,直接往左子树的右边找就行,如果左子树为空,需要找到父辈是往右边的,因为往右边代表自己大于父辈*/
            Node parent = node.parent;
            Node temp = node;
            while (parent != null && parent.left == temp){
                temp = parent;
                parent = temp.parent;
            }
            return parent;
        }
    }
找到删除节点中序遍历的后继

和前驱一样的思路。。

/*找到删除节点的中序后继*/
    private Node findDeleteNodeSucc(Node node){
        if (node == null){
            return null;
        }else if (node.right != null){
            Node succ = node.right;
            while (succ.left != null) {
                succ = succ.left;
            }
            return succ;
        }else{
            Node p = node.parent;
            while (p != null && p.right == node) {
                node = p;
                p = node.parent;
            }
            return p;
        }
    }

红黑树的删除 remove

public V remove(K key){
        //首先找到node
        Node ojb = getNode(key);
        if (ojb == null){
            return null;
        }
        V value = ojb.value;
        //删除
        deleteNode(ojb);
        return value;
    }
getNode

逻辑还是比较好懂的

/*道道key对应的node*/
    public Node getNode(K key) {
        Node begin = root;
        while (begin != null){
            int cmp = begin.key.compareTo(key);
            if (cmp == 0) {
                break;
            }else if (cmp > 0){
                begin = begin.left;
            }else {
                 begin = begin.right;
            }
        }
        return begin;
    }
删除 deleteNode

逻辑还是挺难的, 因为红黑树本身就是一颗二叉树,所以删除节点的类型也和二叉树一样分为三种,删除节点的左右孩子为空、都不为空、只有一个不为空,左右孩子不为空的情况,我们可以使用替换的方式,转化要删除的节点从一个左右孩子都不为空变成要删除一个左右孩子都不能同时不为空的节点
示意图:
在这里插入图片描述
比如现在我们要删除6节点,是不是左右孩子都不为空。我们需要找到删除节点再中序遍历的前驱,这里找到了4,左子树不为空,中序遍历的前驱就是左子树的最大值,那么最大值就是一直往右孩子走,直到右孩子为空,所以4节点的孩子右孩子一定是null,左孩子是不做限制的, 我们把4节点的值复制到6节点,然后再把原先的4节点给他删掉,这样不就把左右孩子都不为空的节点转化成删除左右孩子不可能都不为空的节点

所以说任何红黑树的删除最终只4种情况:
在这里插入图片描述对于前两种情况, 就是要么左孩子为null,要么右孩子为null, 删除节点只用:删除节点父亲节点的左孩子或者右孩子指向删除节点的左孩子或者右孩子。 因为红黑树是黑色平衡的,所以删除红色节点是没有影响的,删除黑色节点需要调整。

继续总结删除黑色节点的所有情况:
在这里插入图片描述
思考:如果删除的节点的孩子节点是红色。 这就说明该节点和孩子节点是在同一个组的,所以可以让孩子节点来变成红色,继续保持黑色平衡

继续筛选:
在这里插入图片描述
最难的红黑树删除节点的是这三种情况:因为这三种情况删除的节点在组都只有自己一个,无法找队友替补自己。。。

删除思路:真的超级难啊, 怎么搞 我这层黑色节点已经没有了,我的父亲节点有几种情况?红或者黑 ,

最终示意图:
在这里插入图片描述
删除4节点的情况如图所示,这里因为4节点要么没有孩子要么只有一个孩子(左或者右)、5节点如果为红,那么5节点的孩子要么没有有的话都是黑色的、5节点如果为黑色,那么孩子节点就随便了。

思考:4节点这个树黑色少了一个,也就是父亲节点7的左子树少了一个,那如果我们把7的右子树的5节点如果是黑我们把他染红,这样7节点的左右子树现在都是黑色平衡了,但是7节点这个树,也就是7节点的父亲的左子树少了一个节点,如果7是红色我们直接把它染黑,那不就都补上去了。。完美。,但是要注意的是什么,如果5这个节点的左右孩子要是随便一个是红色就不能把5从黑色染成红色(出现红红父子关系就不是红黑树了)注意:如果7本来就是黑的,那么染不了黑色,那现在情况就变成了4节点的树缺少了一个黑色节点变成了 7节点的树缺少了一个黑色节点,所以现在7变成不平衡的节点。以7作为调整节点,继续循环处理

所以第一个伪代码编写:
假设删除节点为x
1、如果x的兄弟节点为黑色
1、1 如果x的兄弟节点的左右孩子都不为红色。(注意:为了方便把null的时候的颜色为黑色)
1、1、1 把x的兄弟节点染成红色。
1、1、1、1 x的父亲节点颜色时候为红色,则吧x的父亲节点的颜色染为黑,结束。
1、1、1、1 x的父亲节点的颜色为黑色,x=x的父亲节点
1、2 如果x的兄弟都不是黑色(兄弟节点的孩子节点是红色)
情况如下图:
在这里插入图片描述
我们的目标想法就是吧x也就是图中的4的兄弟节点9所在的组中那一个节点作为x的爷爷节点。那如何做呢。左旋思考:如果直接左旋上面6中情况中那些会存在问题,即图中的2 和 5 ,如下所示:
在这里插入图片描述
所以说需要考虑一个兄弟节点的右孩子为黑色的问题,如果旋转的是黑色,那么无法补偿了, 解决方法,在左旋之前,先对9进行右旋
示意图:
在这里插入图片描述
看到了没有,是不是多了一个红色节点出来,这样在左旋即使是黑导致少了一个,也可以吧9的红色染成黑色进行补偿。所以??? 左旋之后要不要调整吧红色作为黑色进行补偿完全取决于左旋的节点是不是黑色还是红色

2 x的兄弟节点为红色

这种情况比较难想到处理方法,如果x的兄弟节点为红色,那么x的兄弟节点的孩子不可能为红色,要么没有,有的话一定是黑色(红黑树不能能出现红色节点的父亲节点或者孩子节点为红色)
我们现在想的就是吧x的兄弟节点变成黑色。回到逻辑处理1的那种情况。
操作如下:
在这里插入图片描述
现在4 也就是x(逻辑调整点)的兄弟节点是不是由从红色的9变成了黑色或null,经过这样的操作之后如果兄弟节点不为null是不是回到了逻辑1了,如果兄弟节点为null不就那个更加方便了,直接把x的父亲节点从红色染成黑色就行了。。。

注意整体图片展示的逻辑是按照左孩子的调整,如果x是右孩子操作相反,但是思路是一样的

代码结构如下:

/*node 作为调整节点*/
    private void fixAfterRemove(Node node) {
        //说明后继是红色的直接然后替代自己
        if (node.color == RED) {
            node.color = BLACK;/*补充失去的黑色节点*/
            return;
        }
        //自身组无法补充,需要看兄弟节点
        while (true) {
            //node为父亲节点的左孩子
            if (node.parent.left == node) {
                Node nr = node.parent.right;
                //node的兄弟节点为右
                if (colorOf(nr) == RED) {
                    //兄弟节点为红色,说明node的父亲节点和兄弟节点是同一个组的
                    leftRotate(node.parent);//进行左旋转
                    //左旋之后为何保持个个分支黑色数量不变需要进行变色
                    node.parent.color = RED;
                    node.parent.parent.color = BLACK;
                }
                //到这里说明兄弟节点的颜色是为黑色
                //判断兄弟节点能不能变成红色,把我们共同的父亲节点变成黑色。
                if (colorOf(nr) == BLACK || //兄弟节点为null
                        (colorOf(nr.left) == BLACK  //node的左右孩子不能为红色,只能为null或者黑色,如果null我们也是认为黑色
                                && colorOf(nr.right) == BLACK)) {
                    //兄弟节点可以染成红色
                    if (nr != null) {
                        nr.color = RED;
                    }
                    //父亲节点变成黑色
                    if (node.parent.color == RED) {
                        node.parent.color = BLACK;
                        return;
                    } else {
                        //父亲节点不能变成黑色 调整节点变成父亲
                        node = node.parent;
                    }
                } else {
                    //判断node的兄弟节点的右孩子是不是黑色,即有没有红色进行补偿
                    if (colorOf(nr) == BLACK) {
                        //先对孩子节点进行右边旋转。
                        rightRotate(nr);
                        //颜色调整 保持在同一个组
                        nr.color = RED; //从黑变红
                        nr.parent.color = BLACK; //从红变黑
                        //换新的兄弟节点
                        nr = node.parent.right;
                    }
                    //颜色要不要补偿取决于node的父亲节点是不是的颜色
                    //这三句代码的重量不是一般的大。。。。,
                    //这三句代码的意思就是 nr.color之前一定是黑色的,nr.right的颜色也一定是红色的。
                    // nr.parent.color = BLACK;nr.right.color = BLACK; 这两句代码是吧nr.color一定是黑色往左右分支移动了
                    //然后吧nr.right要不要从红变成黑进行补偿,转换成了nr.color的颜色是根据node.parent颜色来改变的。
                    nr.color = node.parent.color;//需不需要补偿
                    nr.parent.color = BLACK;
                    nr.right.color = BLACK;
                    //对node的父亲节点进行左边旋转
                    leftRotate(node.parent);
                    return;
                }
                //node为父亲节点的右孩子
            } else {
                //todo 这里对前面的代码进行复制然后调整一下,就行了
            }
        }
    }

nr.color = node.parent.color;//需不需要补偿
nr.parent.color = BLACK;
nr.right.color = BLACK;

这三句代码的真的太绝了。。。,

总结

到这里红黑树的研究就结束了, 从周五一直研究到周天,也就是今天,诶,怎么说呢, 红黑树的删除时真的很难,红黑树的删除也就4阶B树的删除使用二叉树+颜色节点来表示。真的很难。 笔者能力有限,对最后代码的调试,完善根据呃,估计还得花个半天。。。, 等日后再来完善吧, 红黑树的添加逻辑倒是不怎么难。新增节点的代码已经可以用了,我也测试过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值