红黑树(Red-black tree)

树(tree)的基本知识

一.定义

是一种抽象数据类型,或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合

树.png

二.特点

  • 每个节点有零个或多个子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树

三.存储结构

  1. 线性存储
    /* 树节点的定义 /
    #define MAX_TREE_SIZE 100
    typedef struct
    {
    TElemType data;
    int parent; /
    父节点位置域 /
    } PTNode;
    typedef struct
    {
    PTNode nodes[MAX_TREE_SIZE];
    int n; /
    节点数 */
    } PTree;
树-线性存储.jpg
  1. 链式存储

     /*树的孩子链表存储表示*/
     typedef struct CTNode  // 孩子节点
     { 
         int child; 
         struct CTNode *next;
      } *ChildPtr;
      typedef struct
      {
         ElemType data; // 节点的数据元素 
         ChildPtr firstchild; // 孩子链表头指针
       } CTBox;
       typedef struct 
       {
          CTBox nodes[MAX_TREE_SIZE]; 
          int n, r; // 节点数和根节点的位置
        } CTree;
    
树-链式存储.jpg

红黑树(Red-black tree)的基本知识

一.定义

红黑树是一种自平衡二叉查找树,典型的用途是实现关联数组,它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。

红黑树.png

NIL节点表示数据的结束,在wiki百科中其被当做叶子节点,画图时应该也体现出来。

二.特点

  1. 任意节点的左子树不空,则左子树上所有节点的值均小于它的根结点的值;
  2. 任意节点的右子树不空,则右子树上所有节点的值均大于它的根结点的值;
  3. 任意节点的左、右子树也分别为二叉查找树;
  4. 没有键值相等的节点;
  5. 节点是红色或黑色;
  6. 根是黑色, 所有叶子都是黑色;
  7. 每个红色节点必须有两个黑色的子节点;(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
  8. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点;

通过节点颜色,限制了二叉树的高度。
操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

三.抛出问题

  • O(log n)的操作效率是如何得出的?为什么与高度成比例,而不是与节点数成比例?

一个由n个节点随机构成的二叉查找树的高度为(log n ).证明如下:

红黑树高度证明.jpg

而时间复杂度是以某个基础数据操作的重复次数作为量度。红黑树的是二叉搜索树,左子树上所有节点的值均小于他的根节点的值,右子树上所有节点均大于根节点的值,左右子节树相对根节点按大小分布。如果把每次节点值的比较看成基础数据操作,那么最差的查找情况是一直查找到高度最大的根节点,那么查找的时间复杂度即与高度成正比,可表示成O(log n)

如何生成红黑树

简单了解了红黑树的字面定义,下面动手感受下红黑树的相关操作。当你插入或者删除一个节点时,可能会破坏红黑树的性质,所以需要对树节点进行重新着色或者旋转,来保持红黑树的结构。首先看下二叉树的旋转。

一.旋转

  • 左旋
树左旋.jpg

假设pivot节点不为空,其右子树不为空,那么左旋即是:使pivot的右孩子Y为子树的根,pivot节点为子树根节点的左孩子,pivot左孩子、Y节点的右孩子不改变,Y节点左孩子变为pivot节点右孩子。

  • 右旋
树右旋.jpg

假设pivot节点不为空,其左子树不为空,那么右旋:使pivot的左孩子Y为子树的根,pivot节点为子树根节点的右孩子,pivot的右孩子、Y节点的左孩子不变,Y节点的右孩子变为pivot节点的左孩子。

任意节点的左子树不空,则左子树上所有节点的值均小于它的根结点的值
任意节点的右子树不空,则右子树上所有节点的值均大于它的根结点的值

实战演练之增加、删除节点时,如何保证红黑树的性质不被破坏。

二.增加节点

往一个空的红黑树中,依次插入数据:12 1 9 2 0 11 7 19 4

  • 插入12
+12.jpg

节点为根节点,所以为黑色,两个null节点为黑色节点。

  • 插入 1

+1.jpg

1小于12,所以是根节点的左孩子,如果为黑色,那么违反性质:从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。所以新增节点为红色。

  • 插入 9
+9.jpg

按照二叉搜索树的逻辑,9小于12、大于1,应该是1节点的右孩子。但,新增的两个NIL节点已经使得12,1,9,NI这条路径的黑色节点至少为两个,而12,NIL这条路径的黑色节点只有两个。所以要对1节点进行左旋,9节点变为12节点的左孩子,发现问题还是存在。继续,对12节点进行右旋,9节点为根节点,1、12分别为9节点的左右孩子。尝试着色,9节点必须为黑色,而1,12节点可以为红色,也可以为黑色。

  • 插入 2

+2.jpg

2大于1,直接作为1的右孩子即可。2的增加必然会增加两个黑色NIL节点,所以每条路径至少有三个黑色节点。则9,12,NIL这条路径中,12节点应该变为黑色;9,1,NIL这条路径中,1应该变为黑色。9,1,2,NIL这条路径中,2为空色来使得黑色节点个数为三个。验证一下,满足从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。则颜色调整完毕。

  • 插入 0
+0.jpg

0节点直接作为1节点的左孩子,保持跟2节点相同的颜色即可。左右子树依旧保持平衡。

  • 插入 11


    +11.jpg

    11节点作为12节点的左子树,颜色跟同一层的0,2节点保持一致即可。

  • 插入 7
+7.jpg

从二叉查找树的性质看,7节点作为2节点的右孩子即可。这时来分析着色问题,我们先看最短路径的黑色分布,9,12,NIL这条路径,有三个黑色节点,以此为参考,尝试改变9节点左子树的着色。目前最长的路径是9,1,2,7,NIL这条路径。保持三个黑色节点的话,9跟NIL已经为黑色节点,而红色节点又不能挨着,所以只能是1为红色节点,2为黑色节点,7为红色节点。那么9,1,0,NIIL这条路径,0就要为黑色节点。调整完毕。

  • 插入 19
+19.jpg

19节点作为12节点的右孩子,与左孩子保持一样的红色即可。

  • 插入 4
+4.jpg

4节点应该作为7节点的左子树,无论着什么颜色,以1节点为根节点的子树,都要破坏红黑性质。所以应该进行旋转。先以7为根节点进行一次右旋,再以2为根节点进行一次左旋。尝试着色即可。

思维误区:之前我把左右字数高度相差不能超过1加入到红黑树的性质中,这导致我的推理逻辑发生了错误。因为红黑树是用红黑着色来保证高度,我直接用使用结果来推导红黑着色,这样会忽略红黑树本身的优点,而只关注了平衡二叉树的优点。错误思维路线:我先优先保持二叉搜索树的性质,然后通过旋转使其保持平衡,然后再进行颜色调整。感觉只是在探讨平衡二叉搜索树的性质,红黑性质被弱化了,仅仅是为了构造一个红黑树而调整着色。思维调整:先从二叉搜索树的角度对节点进行插入,然后从着色的角度对树进行旋转。

插入节点的五种情况:
  1. 新节点N位于树的根上,没有父节点。
  2. 新节点的父节点P是黑色情形。
  3. 父节点P、叔叔节点U,都为红色。
  4. 父节点P是红色,叔叔节点U是黑色或NIL; 插入节点N是其父节点P的右孩子,而父节点P又是其父节点的左孩子。
  5. 父节点P是红色,而叔父节点U 是黑色或NIL,要插入的节点N 是其父节点的左孩子,而父节点P又是其父G的左孩子。

这里,先不补充每种情况的操作方法,你是不是能自己动手写下呢?欢迎留言~

三.删除节点

类似插入节点的分析、总结,删除节点也可以针对每种场景找到固定的着色方法,就像玩一个游戏,有自己的推理跟玩法。我先做个PPT,这块稍后补充。

使用场景

所有的插入、删除都是有限个情况,基于插入、删除的情况分析,即可编写算法生成红黑树,使其在固定的业务场景中发挥红黑树稳定操作效率的特色了。

  • 实现关联数组
    关联数组又称映射Map)、字典Dictionary)是一个抽象的数据结构,它包含着类似于(键,值)的有序对。C++的STL中。map和set都是用红黑树实现的。
    底层采用红黑树保存 key-value对,Key值唯一。添加、取出都需要一些循环操作,但所有键值对都是按照key根据指定排序规则保持有序状态。
  • 著名的linux进程调度Completely Fair Scheduler
    CFS用红黑树管理进程控制块
    每个进程都包含运行时间,通过考虑优先级、系统负载等计算出的加权时间,作为红黑树的key。下一个执行的进程为最小运行时间的进程,对应红黑树的最左叶子。
  • epoll在内核中的实现,用红黑树管理事件块
    linux内核的可扩展I/O事件通知机制,应用于高性能网络程序场景,在内核cache中用红黑树储存事件块,保证较快的查询速度。
  • nginx中,用红黑树进行超时管理
    Nginx为网页服务器,在进行超时管理时,通过红黑树存储超时时间对象,每次找到key最小的节点,然后进行判断是否超时,超时就处理,直到取出的未超时的事件。
  • Java的TreeMap实现
    TreeMap是Java中key-value的集合,根据key值的自然顺序进行排序,或者依据比较函数。

二叉搜索树之间的对比

一.AVL树

计算机科学中,AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树。查找、插入和删除在平均和最坏情况下都是O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
节点的平衡因子是它的左子树的高度减去它的右子树的高度(有时相反)。带有平衡因子1、0或 -1的节点被认为是平衡的。带有平衡因子 -2或2的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

AVL旋转.png

红黑树相对于AVL树来说,牺牲了部分平衡性以换取插入/删除操作时少量的旋转操作,整体来说性能要优于AVL树。

未完待续

不提问题的码农不是好程序员。自己写完了红黑树的简单剖析,感觉还是只懂皮毛,没有从触碰到算法的核心内容。所以,不妨留几个小问题,担心自己脑子生锈或者没事想玩手机的时候,再提笔研究下红黑树。

  • 举例说明红黑树如何牺牲部分平衡性来保证尽可能少的旋转操作,通过与AVL树做比较。增加节点的时候,如果是在构建AVL树,结果会怎么样。

参考

教你初步了解红黑树
算法的时间复杂度和空间复杂度-总结
红黑树从头至尾插入和删除
AVL树
红黑树C源码实现与剖析

Echo
8 Nov,2016

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

推荐阅读更多精彩内容

  • 1. 简介 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是二叉查找树的变种之一。它是在1972...
    谢朴欢阅读 1,093评论 0 8
  • 树的概述 树是一种非常常用的数据结构,树与前面介绍的线性表,栈,队列等线性结构不同,树是一种非线性结构 1.树的定...
    Jack921阅读 4,432评论 1 31
  • 1、红黑树介绍 红黑树又称R-B Tree,全称是Red-Black Tree,它是一种特殊的二叉查找树,红黑树的...
    文哥的学习日记阅读 9,849评论 1 35
  • 基于树实现的数据结构,具有两个核心特征: 逻辑结构:数据元素之间具有层次关系; 数据运算:操作方法具有Log级的平...
    yhthu阅读 4,235评论 1 5
  • 这两天,追完前段全民热衷的电视剧《人民的名义》,55集,时间紧,任务重,很多地方都是跳过的。全剧追完,在两个地方我...
    selinaselina阅读 248评论 0 0