数据结构(6)-红黑树和红黑树的插入

红黑树

定义

  1. 每个节点或者是黑色,或者是红色。
  2. 根节点是黑色。
  3. 每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
  4. 如果一个节点是红色的,则它的子节点必须是黑色的。
  5. 从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点


红黑树的应用

红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

红黑树的自平衡

当我们对红黑树进行插入或者删除的时候,为了保证红黑树能够保持特点4(如果一个节点是红色的,则它的子节点必须是黑色的)和特点5(从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点),在每次进行操作的时候,都会做平衡操作,这些平衡操作就是红黑树的左旋右旋

左旋

当对节点x进行左旋时,有三个步骤

  1. 将x的右子树指向y的左子树
  2. 将x的父节点改成y的父节点
  3. 将y的左子树指向x
//具体代码
private void rotateLeft(Tree p) {
    if (p != null) {
        //先保存一份y
        Tree r = p.right;
        //将x的右子树指向y的左子树
        p.right = r.left;
        if (r.left != null)
            r.left.parent = p;
        //将x的父节点改成y的父节点
        r.parent = p.parent;
        if (p.parent == null)
            root = r;
        else if (p.parent.left == p)
            p.parent.left = r;
        else
            p.parent.right = r;
        //将y的左子树指向x
        r.left = p;
        p.parent = r;
    }
}

右旋


右旋的原理基本和左旋相同,明白了左旋,右旋也就不难理解了,具体也是3个步骤

  1. 将y的左子树指向x的右子树
  2. 将y的父节点改成x的父节点
  3. 将x的右子树指向y
//具体代码
private void rotateRight(Tree p) {
    if (p != null) {
        //保存一份x(即y的左子树)
        Tree l = p.left;
        //将y左子树指向x的右子树
        p.left = l.right;
        if (l.right != null) l.right.parent = p;
        //将y的父节点改成x的父节点
        l.parent = p.parent;
        if (p.parent == null)
            root = l;
        else if (p.parent.right == p)
            p.parent.right = l;
        else p.parent.left = l;
        //将x的右子树指向y
        l.right = p;
        p.parent = l;
    }
}

在了解了红黑树的自平衡操作之后,我们就可以对红黑树进行增删了

插入结点

如何在红黑树中插入一个节点呢?

  1. 首先,我们需要按照二叉查找树的方式插入一个节点,因为红黑树本身就是二叉查找树,所以我们插入节点之后它依旧是一课二叉查找树
  2. 根据插入的不同情况进行后续操作
    2.1.1 情况一:插入节点是根节点,直接标黑无需调整
    2.1.2 情况二,插入节点的父节点是黑节点,直接标红无需调整
    2.1.2 情况三:插入节点的父节点是红节点,将插入节点标为红色并进行自平衡调整

插入调整

在进行插入调整时又分为几种情况,让我们从简单到复杂一一说明

情况一:叔叔节点是黑色

这种情况下,又分为两种情况

  1. 插入为左节点


  2. 插入为右节点



    因为插入为右节点时,可以直接通过右旋p节点变为情况一,所以也可以总结为一种情况

这种情况下,插入节点的父节点为红节点,叔叔节点为黑节点,那么祖父节点(G节点)一定是黑节点,所以我们对祖父节点进行右旋,然后将它标红,再将父节点标黑,就完成平衡了

这样做的原因很好理解,插入的节点是红节点,插入节点的父节点也是红节点,这样就违背了定义4(如果一个节点是红色的,则它的子节点必须是黑色的),但是又不能直接将插入节点改为黑节点(因为要满足定义5:从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点),所以我们就以祖父节点为根节点进行调整,调整之前和调整之后祖父节点这条线上的黑色节点总数没变,所以调整完祖父节点就完成平衡了

情况二:叔叔节点为红节点

这种情况稍微比情况一复杂,具体分为几个步骤

  1. 将插入节点的父节点和叔叔节点标位黑色
  2. 将插入节点的祖父节点标位红色
  3. 以插入节点的祖父节点为新的插入节点,然后循环步骤1到步骤3,如果这个过程中出现了情况一,就按照情况一的处理方式进行处理

//具体代码可以参考java中TreeMap的写法

private void fixAfterInsertion(Entry<K,V> x) {
    //先将插入节点标位红节点
    x.color = RED;
    //如果插入节点不是根节点,并且插入节点的父节点是红色
    while (x != null && x != root && x.parent.color == RED) {
        //parentOf(x)是表示x的父节点
        //如果x的父节点是祖父节点的左节点
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            //保存x祖父节点的右节点为y(即y是x的叔叔节点)
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
            //如果叔叔节点是红色
            if (colorOf(y) == RED) {
                //将x的父节点和叔叔节点都设置为黑色
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                //将x的祖父节点设置为红色
                setColor(parentOf(parentOf(x)), RED);
                //将x的祖父节点作为新的插入节点进行平衡调整
                x = parentOf(parentOf(x));
            } else {
             //如果叔叔节点是黑色
                if (x == rightOf(parentOf(x))) {
                    //如果x是父节点的右节点,那么对x父节点进行左旋(情况一的子情况2)
                    x = parentOf(x);
                    rotateLeft(x);
                    //经过这两行代码的处理,子情况2则变成了子情况1
                }
                //将x的父节点设为黑色,x的祖父节点设为红色,对x的祖父节点进行右旋(情况一的子情况1)
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateRight(parentOf(parentOf(x)));
            }
        } else {
            //如果x的父节点是祖父节点的右节点
            //保存x祖父节点的左节点为y(即y是x的叔叔节点)
            Entry<K,V> y = leftOf(parentOf(parentOf(x)));
            //如果叔叔节点是红色
            if (colorOf(y) == RED) {
            //将x的父节点和叔叔节点都设置为黑色
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                 //将x的祖父节点设置为红色
                setColor(parentOf(parentOf(x)), RED);
                 //将x的祖父节点作为新的插入节点进行平衡调整
                x = parentOf(parentOf(x));
            } else {
            //如果叔叔节点是黑色
                if (x == leftOf(parentOf(x))) {
                    //如果x是父节点的左节点,那么对x父节点进行右旋(情况一的子情况2的镜像情况)
                    x = parentOf(x);
                    rotateRight(x);
                }
                //将x的父节点设为黑色,x的祖父节点设为红色,对x的祖父节点进行右旋(情况一的子情况1的镜像情况)
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateLeft(parentOf(parentOf(x)));
            }
        }
    }
    root.color = BLACK;
}

数据结构导读目录

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

推荐阅读更多精彩内容

  • 1、红黑树介绍 红黑树又称R-B Tree,全称是Red-Black Tree,它是一种特殊的二叉查找树,红黑树的...
    文哥的学习日记阅读 9,867评论 1 35
  • 本篇主要写的是结合之前分析的2-3-4树和红黑树之间的联系分析红黑树的插入删除操作的原理。我刚刚开始学红黑树时在网...
    Nier_if阅读 1,352评论 1 6
  • 寻找红黑树的操作手册 前言 二叉树知识点回忆以及整理[https://www.jianshu.com/p/bd3c...
    静默加载阅读 673评论 0 1
  • 梦约红颜笑, 结缘半生晓 相怜清影戏, 比翼翔九霄 注: 愿君安,博得倾城顾思瑶。 许卿圆,觅求知心男儿恋。
    斋尘阅读 180评论 1 3