为什么有红黑树?什么是红黑树?看完这篇你就明白了

吕旷
• 阅读 2052

为什么要有红黑树

想必大家对二叉树搜索树都不陌生,首先看一下二叉搜索树的定义:
二叉搜索树(Binary Search Tree),或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
从理论上来说,二叉搜索树的查询、插入和删除一个节点的时间复杂度均为O(log(n)),已经完全可以满足我们的要求了,那么为什么还要有红黑树呢?
我们来看一个例子,向二叉搜索树中依次插入(1,2,3,4,5,6),插入之后是这样的
为什么有红黑树?什么是红黑树?看完这篇你就明白了
可以看到,在这种情况下,二叉搜索树退化成了链表!!!这时候查询、插入和删除一个元素的时候,时间复杂度变成了O(n),显然这是不能接受的。出现这种情况情况的原因是二叉搜索树没有自平衡的机制,所以就有了平衡二叉树的概念。
平衡二叉树(Balanced Binary Tree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
还是刚刚的例子,假如我们用平衡二叉树来实现的话,插入完元素后应该是下面这样的(不唯一)
为什么有红黑树?什么是红黑树?看完这篇你就明白了
平衡二叉树保证了在最差的情况下,二叉树依然能够保持绝对的平衡,即左右两个子树的高度差的绝对值不超过1。但是这又会带来一个问题,那就是平衡二叉树的定义过于严格,导致每次插入或者删除一个元素之后,都要去维护二叉树整体的平衡,这样产生额外的代价又太大了。二叉搜索树可能退化成链表,而平衡二叉树维护平衡的代价开销又太大了,那怎么办呢?这就要谈到“中庸之道”的智慧了。说白了就是把平衡的定义适当放宽,不那么严格,这样二叉树既不会退化成链表,维护平衡的开销也可以接受。没错,这就是我们要谈的红黑树了。首先看一下红黑树的定义:
红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须除了满足二叉搜索树的性质外,还要满足下面的性质:
性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL)是黑色。
性质4:每个红色结点的两个子结点一定都是黑色。
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
这就是红黑树的五条性质。我相信很多人都看到过,能背下来的也不在少数,但是真正理解为什么要这样定义的恐怕就不多了。下面就从2-3树的角度来谈谈红黑树的定义。

从2-3树来看红黑树

一般我们接触最多的是二叉树,也就是一个父节点最多有两个子节点。2-3树与二叉树的不同之处在于,一个父节点可以有两个子节点,也可以有三个子节点,并且其也满足类似二叉搜索树的性质。还有最重要的,2-3树的所有叶子节点都在同一层,且最后一层不能有空节点,类似于满二叉树。
我们依次插入10,9,8,7,6,5,4,3,2,1来看一下2-3树是如何进行自平衡的。
2-3树在插入元素之前首先要进行一次未命中的查找,然后将元素插入叶子节点中,之后再进行平衡操作,下面具体说明。
首先插入10,如下图
为什么有红黑树?什么是红黑树?看完这篇你就明白了
然后插入9,9小于10,2-3树在插入时要将9融入10这个叶子节点中(当然也是根节点),融合完成后如下:
为什么有红黑树?什么是红黑树?看完这篇你就明白了
这是一个3节点,不用执行平衡操作。2-3树中把有两个元素,三个子节点的节点称为3节点,把有一个元素,两个子节点的的节点称为2节点。
接着插入8,插入8的时候同样要先融入叶子节点中,如下图左侧所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
8融入叶子节点后,该结点便拥有了3个元素,不满足2-3树的定义,这时就要把3节点进行分裂,即把左侧和右侧的元素分裂为2节点,而中间的元素抽出,继续融入其父节点,在这里便成为了一个根节点。
继续插入7,如下
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入后,各个节点都满足2-3树的定义,不需要进行平衡操作。
接着插入6,还是首先找到叶子节点,然后将其融入,如下图左侧所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入后6、7、8三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素7融入父节点,6和8分裂为7的左右子节点。
接着插入5,如下图所示,同样不需要进行平衡操作
为什么有红黑树?什么是红黑树?看完这篇你就明白了
接着插入4,还是首先找到叶子节点,然后将其融入,如下图左侧所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入后4、5、6三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素5融入父节点,4和6分裂为5的左右子节点。5融入父节点后,该结点便有了5、7、9三个元素,因而需要继续分裂,元素7成为新的根节点,5和9成为7的左右子节点。
接着插入3,3融入4所在的叶子节点中,不需要进行平衡操作
为什么有红黑树?什么是红黑树?看完这篇你就明白了
接着插入2,还是首先找到叶子节点,然后将其融入,如下图左侧所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入后2、3、4三个元素所在的叶子节点不再满足2-3树的定义,需要进行分裂,即抽出元素3融入父节点,2和4分裂为3的左右子节点,3融入5所在的父节点中。
最后插入2,同样先找到叶子节点,然后将其融入,如下图所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
至此,我们便完成了在2-3树中依次插入10,9,8,7,6,5,4,3,2,1,并且2-3树始终维护着平衡。怎么样,是不是很神奇。

再看红黑树

那么红黑树与2-3树有什么关系呢?现在我们对2-3树进行改造,改造成一个二叉树。怎么改造呢?对于2节点,保持不变;对于3节点,我们首先将3节点中左侧的元素标记为红色,如下图2所示。
为什么有红黑树?什么是红黑树?看完这篇你就明白了
然后我们将其改造成图3的形式;再将3节点的位于中间的子节点的父节点设置为父节点中那个红色的节点,如图4的所示;最后我们将图4的形式改为二叉树的样子,如图5所示。图5是不是很熟悉,没错,这就是我们常常提到的大名鼎鼎的红黑树了。
下面我们回过头再看下红黑树的五条性质。
为什么有红黑树?什么是红黑树?看完这篇你就明白了
性质1:每个节点要么是黑色,要么是红色。
2-3树中存在2节点和3节点,3节点中左侧的元素便是红色节点,而其他的节点便是黑色节点。
性质2:根节点是黑色。
在2-3树中,根节点只能是2节点或者3节点,2节点与3节点在红黑树中的等价形式,如下图所示
为什么有红黑树?什么是红黑树?看完这篇你就明白了
显然,无论是哪种情况,根节点都是黑色的。
性质3:每个叶子节点(NIL)是黑色。
这里的叶子节点不是指左右子树为空的那个叶子节点,而是指节点不存在子节点或者为空节点被称作叶子节点。在性质2中我们讨论的根节点是黑色的都是讨论根节点不为空的情况,若红黑树是一个空树,那么根节点自然也是空的叶子节点,这时候叶子节点便必然是黑色的。
性质4:每个红色结点的两个子结点一定都是黑色。
还是从2-3树的角度来理解,红色节点对应2-3树中3节点左侧的元素,那么它的子节点要么是2节点,要么是3节点。无论是2节点还是3节点对应的节点颜色都是黑色的,这在性质2时已经讨论了。
性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
性质5应该是红黑树最重要的一条性质了。2-3树是一颗绝对平衡的树,即2-3树中任意一个节点出发,到达叶子节点后所经过的节点数都是一样的。那么对应到红黑树呢?2-3树中2节点对应到红黑树便是一个黑色的节点,而3节点对应到红黑树是一个红色节点和一个黑色节点。所以,无论是2节点还是3节点,在红黑树中都会对应一个黑色节点。那么2-3树中的绝对平衡,在红黑树中自然就是任意一结点到每个叶子结点的路径都包含数量相同的黑结点了。
相信大家现在已经对红黑树的五条性质有了更加深刻的体会了。那么我们再看下红黑树维持平衡的三种操作,即变色、左旋、右旋怎么理解呢?
首先看一下变色,以下图为例,
为什么有红黑树?什么是红黑树?看完这篇你就明白了
在2-3树中插入节点3后,便不再满足2-3树的定义,需要进行分解,将元素2抽出作为1和3的父节点,然后2继续向上融合。
对应到红黑树中就是,首先插入节点3,在红黑树中新插入的节点默认为红色,然后不满足定义,所以需要进行分解,分解后各个节点都为2节点,所以变为黑色。而2节点需要继续向上融合,故要变成红色。
接着看一下右旋转,以下图为例,
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入元素1后,进行右旋转操作,首先把2节点与3节点断开连接,同时把2与2的右子树断开连接,然后把2的右子树连接至3的左子树位置,不会违背二分搜索树的性质,然后再把3连接至2的右子树位置。最后还要改变对应节点的颜色,即把2节点的颜色改为原来3节点的黑色,把3节点的颜色改为原来2节点的红色。
接着看一下左旋转,与右旋转类似,以下图为例,
为什么有红黑树?什么是红黑树?看完这篇你就明白了
插入元素3后,进行左旋转操作,首先把2节点与3节点断开连接,同时把3与3的左子树断开连接,然后把3的左子树连接至2的右子树位置,不会违背二分搜索树的性质,然后再把2连接至3的左子树位置。最后还要改变对应节点的颜色,即把2节点的颜色改为原来3节点的红色,把3节点的颜色改为原来2节点的黑色。

写在最后

最后需要说的是,本文中提到的红黑树是一种特殊的红黑树——左倾红黑树,即红色节点都是父节点的左子树,其实按照红黑树的定义不必这样。只要满足红黑树的五条性质,就是红黑树,比如完全可以实现右倾红黑树等等,希望大家不要有误解。
更多关于红黑树的知识,比如红黑树的插入、删除操作,限于篇幅,本文不再介绍,有兴趣的还是推荐大家阅读《算法4》或者《算法导论》。
推荐阅读
《深入浅出话数据结构》系列之什么是B树、B+树?为什么二叉查找树不行?
都2020年了,听说你还不会归并排序?手把手教你手写归并排序算法
为什么会有多线程?什么是线程安全?如何保证线程安全?

更多关于算法、数据机构和计算机基础知识的内容,欢迎大家扫码关注我的公众号“超悦编程”。
为什么有红黑树?什么是红黑树?看完这篇你就明白了

点赞
收藏
评论区
推荐文章
22 22
4年前
动图图解二叉查找树的基本原理及其实现
本文为系列专题的第12篇文章。1.2.3.4.5.6.7.8.9.10.1.是什么?二叉查找树(BinarySearchTree)必须满足以下特点:若左子树不为空,则左子树的所有结点值皆小于根结点值若右子树不为空,则右子树的所有结点值皆大于根结点值左右子树也是二叉排序树如下图,是一颗二叉查找树:如果你对二叉查找树进行中序
似梦清欢 似梦清欢
2年前
树与二叉树
二叉树层次建树二叉树(BinaryTree)是由有限个结点组成的集合,它或者为空集合,或者仅含一个根结点,或者由一个根结点和两棵互不相交的左、右子树组成。树为空即根节点为空。二叉树的基本形态如下图:a:空的二叉树,treeNULL。b:只有空节点的二叉树,
Wesley13 Wesley13
3年前
PTA 7
将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。输入格式:输入第一行给出一个不超过20的正整数N;第二行给出N个互不相同的正整数,其间以空格分隔。输出格式:将输入的N个正整数顺序插入一个初始为空的二叉搜索树。在第一
Kubrnete Kubrnete
4年前
二叉树题集(持续更新中)
对于二叉搜索树,我们规定任一结点的左子树仅包含严格小于该结点的键值,而其右子树包含大于或等于该结点的键值。1\.求二叉搜索树最大深度输入格式:输入给出一行整数序列作为二叉搜索树的键值,数字间以空格分隔,输入0结束(0不计入该二叉树键值)。输入样例:8685109110输出样例:4常规的求二叉搜索树深度的做法是递
高级java面试题,附答案+考点
蚂蚁金服一面1.两分钟的自我介绍2.二叉搜索树和平衡二叉树有什么关系,强平衡二叉树(AVL树)和弱平衡二叉树(红黑树)有什么区别3.B树和B树的区别,为什么MySQL要使用B树4.HashMap如何解决Hash冲突5.epoll和poll的区别,及其应用场景6.简述线程池原理,FixedThreadPoo
zhenghaoz zhenghaoz
4年前
算法笔记:红黑树
红黑树,一种平衡二叉树,最为著名的应用就是CSTL中的map,是有序集合最为理想的存储方式之一。除了二叉树所具有的属性之后,红黑树中每个节点多了一个“颜色”属性,可以是红色或者是黑色。一棵红黑树应该满足一下的性质:1.每个节点是红色或者黑色的;2.根节点是黑色的;3.每个叶节点nil是黑色的(使用哨兵节点在删除调整时可以方便不少);4.如
Wesley13 Wesley13
3年前
JAVA递归实现线索化二叉树
JAVA递归实现线索化二叉树基础理论首先,二叉树递归遍历分为先序遍历、中序遍历和后序遍历。先序遍历为:根节点左子树右子树中序遍历为:左子树根节点右子树后序遍历为:左子树右子树根节点(只要记住根节点在哪里就是什么遍历,且都是先左再右)线索化现在有这么一棵二叉树,它的数据结
Wesley13 Wesley13
3年前
Java实现 LeetCode 814 二叉树剪枝 (遍历树)
814\.二叉树剪枝给定二叉树根结点root,此外树的每个结点的值要么是0,要么是1。返回移除了所有不包含1的子树的原二叉树。(节点X的子树为X本身,以及所有X的后代。)示例1:输入:\1,null,0,0,1\输出:\1,null,0,null,1\解释:
Stella981 Stella981
3年前
LeetCode(98): 验证二叉搜索树
Medium!题目描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。一个二叉搜索树具有如下特征:节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。示例 1:输入:2/\
Wesley13 Wesley13
3年前
PHP数据结构与算法:二叉树
一、定义二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(leftsubtree)和“右子树”(rightsubtree)。二、特性1.在二叉树的第i层上至多有2^(i1)个结点(i0)2.深度为k的二叉树至多有2^k1个结点(k0)3.对于任意一棵二叉树,如果其叶结点数为N0,而
Wesley13 Wesley13
3年前
98. 验证二叉搜索树
题目描述给定一个二叉树,判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征:节点的左子树只包含小于当前节点的数。节点的右子树只包含大于当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。输入:5/\14 /\