数据结构之红黑树

LogicLuminary
• 阅读 2539

红黑树

什么是红黑树

红黑树是一种常见的自平衡二分搜索树,常用于关联数组、字典,在各种语言的底层实现中被广泛应用。

满足红黑树的五个性质

  1. 每个节点是红色或者是黑色
  2. 根节点是黑色
  3. 每一个叶子节点(最后的空节点)是黑色
  4. 如果一个节点是红色的,那么它的孩子节点都是黑色的
  5. 从任意一个节点到叶子节点,经过的黑色节点是一样的

2-3树的定义

红黑树和2-3树有相似之处,我们先了解一下什么是2-3树

满足二分搜索树的基本性质,但是2-3树并不是二叉树

2-3树有两个节点,一个可以存放1个元素,一个可以存放2个元素

满足2-3树的三个性质

  1. 满足二叉搜索树的性质
  2. 节点可以存放一个或两个元素
  3. 每个节点有两个或三个子节点

数据结构之红黑树

每个节点都有2个子节点或者3个子节点,所以就叫做2-3树

左边节点都比42小,而右边节点都比42大,所以是满足二分搜索树的性质。注意,13和33这个节点中间是18,是比17大同时比33小。

数据结构之红黑树

2-3树的绝对平衡性

对于2-3树来说任意一个节点,左右子树的高度一定是相等的,而类似像AVL这种平衡二叉树左右子树的高度相差不超过1。

而2-3树是如何维持绝对平衡性,就要从它的添加操作说起。

对于一颗空树我们添加一个节点42,他既是根节点也是叶子节点,这个时候如果我们想再添加一个节点37应该添加到那里呢?

数据结构之红黑树

按平衡二叉树结构的逻辑来讲37比42小,所以添加到42的左孩子的位置。

但是在2-3树的中并不是这样的,

在2-3中我们依然是从根节点出发,请注意在2-3树中新的节点永远不会添加进空的位置

发现42节点的左子树为空,2-3树就会找到最后一个叶子节点上也就是42的位置,这时候37就会和42进行融合,从一个2节点变为了3节点。这个时候还是一颗绝对平衡的二叉树。

数据结构之红黑树

如果这个时候我们再添加一个12节点的话,因为12节点比37小,所以去找37的左孩子,但是我们已经知道,37的左孩子为空,而2-3并不会将新的节点添加到空的位置中去,这个时候我们就会变成4节点

数据结构之红黑树

但是在2-3中最多支持3个节点所以这个时候就会进行分裂,同时保持绝对平衡。

数据结构之红黑树

如果这个时候我们再添加一个18节点,我们可以很清楚的知道18会找到12节点但是12节点的子节点都为空,所以18就会和12节点进行融合。

数据结构之红黑树

如果这个时候我们再添加一个6节点,大家很清楚的知道是会和12节点、18节点进行融合,这个时候变成了4节点,所以要进行分裂成为下图这样。

数据结构之红黑树

但是!这样我们就不是绝对平衡的二叉树了!

这个时候就需要我们12节点去找他的父节点,发现12的父节点37是一个2节点,这个时候就简单了,我们只需要让12和37节点进行融合为3节点,然后让6变成12和37的左孩子,让18节点变成中间孩子节点即可!

数据结构之红黑树

这个时候我们依次加入5和11节点,就会变成下图这样。

数据结构之红黑树

因为不满足2-3树的性质,所以我们还是将他们进行分裂。

数据结构之红黑树

但是这个时候又不是绝对平衡了,所以我们需要让6去找父亲节点,但是父亲节点已经是3节点了,不过没关系,我们照样把他融合进去成下图这样。

数据结构之红黑树

这个时候我们需要进行分裂,我们需要把5和11分别放在6的左孩子和右孩子的位置,37也同理,形成下图这样。依然保持着绝对平衡性。

数据结构之红黑树

其实我们可以分为两种情况去理解,一种就是当前节点为3节点而父节点为2节点。

数据结构之红黑树

另一种就是当前节点为3节点,父节点也为3节点的情况。

数据结构之红黑树

红黑树和2-3树的等价性

在2-3树种,我们可以表示2个节点和3个节点。

数据结构之红黑树

在红黑树中,我们可以这样表示,然后用红色的线来表示bc是融合的,同时b是比c小的。由于红色节点是由3节点拆分而来,因此所有的红色节点都只会出现在左子树上。

数据结构之红黑树

在下图中只有6和17和66是红色节点,我们很容易得知,因为他们都是3节点。

数据结构之红黑树

如果抽象的话可以红色节点上移一下,我们可以看到其实是和2-3树是一样的。

数据结构之红黑树

保持根节点为黑色和左旋

在下图上,我们知道最后分裂以后,6继续会向上进行融合,我们知道向上融合的节点一定是红色的,所以我们知道6是红色的,但发现已经没有父亲节点的时候,我们将6变为黑色。

数据结构之红黑树

最开始的时候我们想添加一个42的话,我们直接让42当根节点就好了,然后我们需要让42变为黑色节点。

数据结构之红黑树

这个时候我们添加一个37,可以直接放入到42左孩子的位置。

数据结构之红黑树

但是,如果我们要插入的节点为根节点的右孩子的节点的时候,我们需要进行左旋。

数据结构之红黑树

数据结构之红黑树

伪代码如下:

node.right = x.left;
x.left = node;
x.color = node.color;
node.color = red;

Java代码如下:

    //   node                     x
    //  /   \     左旋转         /  \
    // T1   x   --------->   node   T3
    //     / \              /   \
    //    T2 T3            T1   T2
    private Node leftRotate(Node node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

颜色翻转和右旋

最开始的时候我们想添加一个42的话,我们直接让42当根节点就好了,然后我们需要让42变为黑色节点。

数据结构之红黑树

这个时候我们添加一个37,可以直接放入到42左孩子的位置。

如果这时候我们再向红黑树中加入66节点,会变成如下图这样。

数据结构之红黑树

相应的在2-3树中是这个样子。

数据结构之红黑树

在红黑树中红色节点的意思就是和父节点是融合在一起的。

而37和66都是红色节点,说明它们都和父亲节点是融合在一起的。

在2-3树中,我们处理4节点的时候会进行分裂,分裂为3个2节点。

数据结构之红黑树

但是在红黑树中其实黑色节点就是2个节点,我们不需要进行旋转操作,我们只需要让37和66进行变色为黑色,就和2-3树中节点的性质是一样的了。

数据结构之红黑树

在2-3树中,我们需要继续让42节点向上去找父亲节点进行融合,相应的在红黑树中我们只需要让42变为红色即可。

数据结构之红黑树

这个就是颜色翻转(flipColors)

     //颜色翻转
    private void flipColors(Node node){
        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

另外一种情况就是,我们树添加成这个样子。我们添加了12节点,其实可以理解为它们是融合在一起的。在2-3树种就是4节点,然后将它进行分裂。

数据结构之红黑树

但是在红黑树中我们怎么变成这个样子呢?其实很简单,我们只需要进行右旋转即可。

为了方便理解,我们引入T1和T2虚拟节点来演示。

数据结构之红黑树

我们将x的右节点t1放到node的左孩子上,然后将node放在x的右孩子的位置即可。

数据结构之红黑树

别忘了,在这个时候node(42)节点其实是融合在x(37)节点上的,所以它要改为红色,37要改为黑色。

对应的伪代码:

node.left = t1;
x.right = node;
x.color = node.color;
node.color = red;

最终我们就可以得到和2-3树结构一样的红黑树了。

数据结构之红黑树

    //       node                            x
    //       / \                           /   \
    //      x   T2     向右旋转 (y)        y     node
    //     / \       - - - - - - - ->          / \
    //    y   T1                              T1  T2
    private Node rightRotate(Node node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

在红黑树中添加新元素

其实还有一种情况我们在前面没有讲到,就是在下图中添加一个元素40应该怎么做。

数据结构之红黑树

根据二分搜索树的性质,我们会将40添加进37的右孩子的位置。

数据结构之红黑树

但是这个时候已经破坏了红黑树的性质,所以我们需要进行左旋。

数据结构之红黑树

这个时候我们还需要进行右旋。

数据结构之红黑树

然后我们需要将40变黑色,42变为红色。

数据结构之红黑树

最后我们进行颜色翻转即可维持红黑树的性质。

数据结构之红黑树

总结图片如下:

数据结构之红黑树

如果我们添加的第三个元素是最小的话,我们可以直接从右旋开始。

数据结构之红黑树

反之我们添加的如果是最大的元素,我们可以直接进行颜色翻转。

数据结构之红黑树

    // 判断节点node的颜色
    private boolean isRed(Node node){
        if(node == null) {
            return BLACK;
        }
        return node.color;
    }


    //   node                     x
    //  /   \     左旋转         /  \
    // T1   x   --------->   node   T3
    //     / \              /   \
    //    T2 T3            T1   T2
    private Node leftRotate(Node node){
        Node x = node.right;
        node.right = x.left;
        x.left = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

    //颜色翻转
    private void flipColors(Node node){
        node.color = RED;
        node.left.color = BLACK;
        node.right.color = BLACK;
    }

    //       node                            x
    //       / \                           /   \
    //      x   T2     向右旋转 (y)        y     node
    //     / \       - - - - - - - ->          / \
    //    y   T1                              T1  T2
    private Node rightRotate(Node node){
        Node x = node.left;
        node.left = x.right;
        x.right = node;
        x.color = node.color;
        node.color = RED;
        return x;
    }

    // 向红黑树中添加新的元素(key, value)
    public void add(K key, V value){
        root = add(root, key, value);
        //根节点为黑色
        root.color = BLACK;
    }

    // 向以node为根的红黑树中插入元素(key, value),递归算法
    // 返回插入新节点后红黑树的根
    private Node add(Node node, K key, V value){

        if(node == null){
            size ++;
            return new Node(key, value); //默认插入红色节点
        }

        if(key.compareTo(node.key) < 0) {
            node.left = add(node.left, key, value);
        } else if(key.compareTo(node.key) > 0) {
            node.right = add(node.right, key, value);
        } else // key.compareTo(node.key) == 0
        {
            node.value = value;
        }

        //右孩子为红色,左孩子为黑色,进行左旋
        if(isRed(node.right) && !isRed(node.left)){
            node = leftRotate(node);
        }
        // 左孩子为红色,并且左孩子的左孩子也是红色,进行右旋
        if(isRed(node.left) && isRed(node.left.left)){
            node = rightRotate(node);
        }
        //颜色翻转
        if(isRed(node.left) && isRed(node.right)){
            flipColors(node);
        }

        return node;
    }

时间复杂度分析

红黑树相比于AVL树,其实是牺牲了平衡性的,红黑树并不完全满足平衡二叉树的定义,红黑树的最大高度达到了2logn的高度,红色节点影响了红黑树的的平衡性。红黑树虽然牺牲了一定的查询性能,但是在增删改操作的性能得到了弥补,红黑树的综合性能还是要优于AVL树的。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java集合系列之HashMap源码
java集合系列之HashMap源码HashMap的源码可真不好消化!!!首先简单介绍一下HashMap集合的特点。HashMap存放键值对,键值对封装在Node(代码如下,比较简单,不再介绍)节点中,Node节点实现了Map.Entry。存放的键值对的键不可重复。jdk1.8后,HashMap底层采用的是数组加链表、红黑树的数据结构,因此实现起
推荐程序员面试秘籍!2021年大厂Java岗面试必问
01JAVA基础1.1java知识点Hashmap源码级掌握,扩容,红黑树,最小树化容量,hash冲突解决,有些面试官会提出发自灵魂的审问,比如为什么是红黑树,别的树不可以吗;为什么8的时候树化,4不可以吗,等等concureentHashMap,段锁,如何分段,和hashmap在hash上的区别,性能,等等HashTable,同步锁,这块可
Wesley13 Wesley13
3年前
java语言基础6
hashmap的数据结构,HashMap的数据结构是数组链表红黑树(红黑树sinceJDK1.8)。我们常把数组中的每一个节点称为一个桶。当向桶中添加一个键值对时,首先计算键值对中key的hash值,以此确定插入数组中的位置,但是可能存在同一hash值的元素已经被放在数组同一位置了,这种现象称为碰撞,这时按照尾插法(jdk1.7及以前为头插法)的方式添
Wesley13 Wesley13
3年前
java集合之HashMap源码解读
源自:jdk1.8.0\_121HashMap继承自AbstractMap,实现了Map、Cloneable、Serializable。HashMap内部是由数组、链表、红黑树实现的变量//默认大小staticfinalintDEFAULT_INITIAL_CAPACI
深入理解跳表及其在Redis中的应用
跳表可以达到和红黑树一样的时间复杂度O(logN),且实现简单,Redis中的有序集合对象的底层数据结构就使用了跳表。其作者威廉·普评价:跳跃链表是在很多应用中有可能替代平衡树的一种数据结构。本篇文章将对跳表的实现及在Redis中的应用进行学习。
zhenghaoz zhenghaoz
4年前
算法笔记:红黑树
红黑树,一种平衡二叉树,最为著名的应用就是CSTL中的map,是有序集合最为理想的存储方式之一。除了二叉树所具有的属性之后,红黑树中每个节点多了一个“颜色”属性,可以是红色或者是黑色。一棵红黑树应该满足一下的性质:1.每个节点是红色或者黑色的;2.根节点是黑色的;3.每个叶节点nil是黑色的(使用哨兵节点在删除调整时可以方便不少);4.如
Stella981 Stella981
3年前
B+树原理以及Java代码实现
最初查找二叉树,由于树的高度会随着有序序列输入而急剧增长,后来出现平衡二叉树,红黑树。B树可以海量数据的快速查询检索,B树主要分为B树(B树),B树,B\树等。B树(B树)M路搜索树,参数M定义节点的分支个数;对于根节点孩子数目为\2,M\,对于其余节点孩子数目为\M/2,M\;每个节点含有关键字属性,至少M/21
Wesley13 Wesley13
3年前
Java集合,TreeMap底层实现和原理
概述文章的内容基于JDK1.7进行分析,之所以选用这个版本,是因为1.8的有些类做了改动,增加了阅读的难度,虽然是1.7,但是对于1.8做了重大改动的内容,文章也会进行说明。TreeMap实现了SotredMap接口,它是有序的集合。而且是一个红黑树结构,每个keyvalue都作为一个红黑树的节点。如果在调用TreeMap的构造函数时没有指定
Wesley13 Wesley13
3年前
Java数据结构和算法(十五)——无权无向图
前面我们介绍了树这种数据结构,树是由n(n0)个有限节点通过连接它们的边组成一个具有层次关系的集合,把它叫做“树”是因为它看起来像一棵倒挂的树,包括二叉树、红黑树、234树、堆等各种不同的树,有对这几种树不了解的可以参考我前面几篇博客。而本篇博客我们将介绍另外一种数据结构——图,图也是计算机程序设计中最常用的数据结构之一,从数学意义上讲
Wesley13 Wesley13
3年前
B
BTreeBTree又叫做B树,和平衡二叉树不同的地方在于B树是多叉树(平衡多路查找树),Oracle和MongoDB的索引技术就是基于B树的数据结构,B树也可以看作是对23查找树的一种扩展。一个m阶的BTree有以下性质1.每个节点最多有m个子节点;2.每个非叶子节点(根节点除外)至少含有m/2个子节点;3.
深入理解经典红黑树 | 京东物流技术团队
本篇我们讲红黑树的经典实现,Java中对红黑树的实现便采用的是经典红黑树。前一篇文章我们介绍过左倾红黑树,它相对来说比较简单,需要大家看完上篇再来看这一篇,因为旋转等基础知识不会再本篇文章中赘述。本篇的大部分内容参考《算法导论》和Java实现红黑树的源码,