二叉搜索树(3)之红黑树 笔记

一、红黑树性质

  1. 节点是黑色或者是红色
  2. 根节点是黑色
  3. 叶子节点都是黑色(叶子节点指的是外部节点或者称之为空节点)
  4. 红色节点的子节点都是黑色节点(红节点的父节点肯定是黑节点。不会出现连续两个红节点)
  5. 从任意节点到叶子节点的所有路径都包含相同数量的黑色节点

二、红黑树的添加

  • 红黑树的添加的都是叶子节点。添加完维护红黑树的五条性质(也就保证了此树的平衡性),下面先把添加之后维护平衡的代码贴出来。
    protected void afterAdd(Node<E> node) {
        
        Node<E> parent = node.parent;

        // 添加的是根节点 或者 上溢到达了根节点
        if (parent == null) {
            black(node);
            return;
        }
        
        // 如果父节点是黑色,直接返回 
        if (isBlack(parent)) return;
        
        // 叔父节点
        Node<E> uncle = parent.sibling();
        // 祖父节点
        Node<E> grand = red(parent.parent);
        if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
            black(parent);
            black(uncle);
            // 把祖父节点当做是新添加的节点
            afterAdd(grand);
            return;
        }
        
        // 叔父节点不是红色
        if (parent.isLeftChild()) { // L
            if (node.isLeftChild()) { // LL
                black(parent);
            } else { // LR
                black(node);
                rotateLeft(parent);
            }
            rotateRight(grand);
        } else { // R
            if (node.isLeftChild()) { // RL
                black(node);
                rotateRight(parent);
            } else { // RR
                black(parent);
            }
            rotateLeft(grand);
        }
    }
  1. 添加的如果是根节点 或者上溢到了根节点只需将根节点染黑即可(添加的根节点染黑不用解释了,上溢到根节点如下图)


    1、上溢到根节点
  2. 添加节点的父节点为黑色时 则return。(此时不影响红黑树的性质)

  3. 父节点为红色,叔父节点为红色时,则出现上溢,父节点叔父节点染黑,祖父节点染红,递归调用本方法把祖父节点传进去。(可参考 1 图 ,如果为上溢到根节点,则继续判断父节点与叔父节点颜色,严么上溢要么旋转保持平衡)

  4. 父节点为红色,叔父节点为黑色。如图所示,父节点在祖父节点的左边,添加节点在父节点的右边。自己染黑,祖父节点染红。
    父节点左旋,祖父节点右旋。即可维持平衡。
    (1、父节点在祖父节点的左边,添加节点在父节点的左边。
    2、父节点在祖父节点的右边,添加节点在父节点的左边。
    3、父节点在祖父节点的右边,添加节点在父节点的右边。
    等情况也是通过染色旋转解决)


    2、旋转操作.png

三、红黑树的删除

  • 红黑树真正删除的都是度为1的节点或者是叶子节点(先贴代码,在讲解)
    protected void afterRemove(Node<E> node) {
        // 如果删除的节点是红色
        // 或者 用以取代删除节点的子节点是红色
        if (isRed(node)) {
            black(node);
            return;
        }
        
        Node<E> parent = node.parent;
        // 删除的是根节点
        if (parent == null) return;

        // 删除的是黑色叶子节点【下溢】
        // 判断被删除的node是左还是右
        boolean left = parent.left == null || node.isLeftChild();
        Node<E> sibling = left ? parent.right : parent.left;
        if (left) { // 被删除的节点在左边,兄弟节点在右边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateLeft(parent);
                // 更换兄弟
                sibling = parent.right;
            }
            
            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    afterRemove(parent);
                }
            } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
                // 兄弟节点的左边是黑色,兄弟要先旋转
                if (isBlack(sibling.right)) {
                    rotateRight(sibling);
                    sibling = parent.right;
                }
                
                color(sibling, colorOf(parent));
                black(sibling.right);
                black(parent);
                rotateLeft(parent);
            }
        } else { // 被删除的节点在右边,兄弟节点在左边
            if (isRed(sibling)) { // 兄弟节点是红色
                black(sibling);
                red(parent);
                rotateRight(parent);
                // 更换兄弟
                sibling = parent.left;
            }
            
            // 兄弟节点必然是黑色
            if (isBlack(sibling.left) && isBlack(sibling.right)) {
                // 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
                boolean parentBlack = isBlack(parent);
                black(parent);
                red(sibling);
                if (parentBlack) {
                    afterRemove(parent);
                }
            } else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
                // 兄弟节点的左边是黑色,兄弟要先旋转
                if (isBlack(sibling.left)) {
                    rotateLeft(sibling);
                    sibling = parent.left;
                }
                
                color(sibling, colorOf(parent));
                black(sibling.left);
                black(parent);
                rotateRight(parent);
            }
        }
    }
  1. 如果删除的节点是叶子节点并且为红色 或者 用以替代删除节点为红色时 染黑该节点 return;(1、删除的节点是叶子节点并且为红色此时父节点指点该节点的已经被清空 在染红此节点也没问题。2、用以替代删除节点为红色时,证明删除的节点度为1 并且替代它的是他的子节点,子节点并且是叶子节点,删除时此节点的父节点直接指向子节点,此时染黑替代的节点,也没问题)
  2. 删除的是根节点,此时 root 已经被清空 直接 return
  3. 到这里就只剩 删除黑色叶子了 这一步很复杂我分步讲解
    1、被删除节点在父节点的左边或者右边 两种情况

    2、兄弟节点为红色时,需要调整,让兄弟节点变为黑色


    1、删除黑色叶子兄弟节点为红.png

    3、此时删除黑色叶子节点的兄弟节点必为黑色 。
    (1、兄弟节点的左右子节点为黑色 则表明兄弟节点不能借给被删除节点 节点,父节点下来与兄弟节点合并,这时父节点如果是红色则父节点染黑,兄弟节点染红。如果父节点为黑,则父节点产生下溢,递归调用自身方法(研究表明下溢情况不会连续下溢三次以上))
    (2、来到这里兄弟节点必有一个或两个红色节点,然后通过染色,旋转即可恢复平衡)


右的逻辑和左的一样对称着写(想)就行

红黑树的平衡

  • 红黑树没有做到完全平衡,没有一条路径是其他路径的两倍以上
  • 黑色节点是完全平衡的
  • 最大高度是 2 * log2(n + 1)

红黑树平均时间复杂度

  • 搜索: O(logn) | 最大搜索次数:log2(n + 1)
  • 添加: O(logn) | 最大搜索次数:log2(n + 1) | 旋转 O(1)次的旋转
  • 删除: O(logn) | 最大搜索次数:log2(n + 1) | 旋转O(1)次的旋转

AVL平均时间复杂度

  • 搜索: O(logn) | 最大搜索次数:log2(n + 1)
  • 添加: O(logn) | 最大搜索次数:log2(n + 1) | 旋转:O(1)次的旋转
  • 删除: O(logn) | 最大搜索次数:log2(n + 1) |旋转:O(log2(n + 1))次的旋转
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353