以2-3-4树解析红黑树

由于红黑树就是2-3-4树的具体实现,所以我根据2-3-4树来讲解红黑树的各类操作规则。

一、2-3-4树

Ⅰ、2-3-4树的定义
  1. 每个节点每个节点有1、2或3个key,分别称为2(孩子)节点,3(孩子)节点,4(孩子)节点。

  2. ⭐所有空链接到根节点的距离都是相同的,这个性质间接保证了红黑树的性质5。空链接即指向一棵空树的链接,如下图。

  3. 每个节点的key从左到右保持了从小到大的顺序,两个key之间的子树中所有的key一定大于它的父节点的左key,小于父节点的右key。

Ⅱ、2-3-4树的自底向上分裂插入

2-3-4树的自底向上插入简单来说就是一个**(向上)分裂**的过程。

①、插入可能出现的情况
  1. 若插入key后该节点key个数小于4,不用操作。

  2. 若插入key后该结点的key个数等于4,由于2-3-4树的最多3个key的定义,此时就要向上分裂,即将插入新key之前的结点中键插入到该结点的父结点中;而若父节点的key个数也等于4,则要继续向上分裂,不断地循环此过程。当根节点也要进行向上分裂,此时树高就会加一。

二、从2-3-4树到红黑树

Ⅰ、红黑树的性质
  1. 节点是红色或黑色。

  2. 根节点是黑色。

  3. 所有叶子都是黑色。(叶子是NUIL节点)

  4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)看看

    2-3-4树的等价表示就知道,不可能有连续两个红色节点,因为这无法用2-3-4树表示出来。

  5. 从任一节点到其每个NIL叶子的所有路径都包含相同数目的黑色节点。(2-3-4树的性质3的间接保证)

Ⅱ、2-3-4树的等价表示
  • 在红黑树中,2-结点的表示与2-3-4树一致
  • 在红黑树中,3-结点的表示如下:
  • 在红黑树中,4-结点的表示如下:
Ⅲ、旋转

当我们实现一些操作时(合并和修复),需要一些局部变换以改变树中节点的状态,此时就可以使用旋转这个局部变换操作来实现

①左旋

在这里插入图片描述

②右旋

在这里插入图片描述

③注意事项

建议用红链接推出旋转后的颜色而不是单纯的记红节点变化情况,作图的时候尽量将红链接标上就不会犯迷糊

⭐Ⅳ、自顶向下分裂的插入操作

在2-3-4树中我讲的是插入新key后,树从插入key的结点自底向上分裂以保证2-3-4树的性质。而在红黑树这里我换一种思路:在红黑树向下查找插入位置的递归过程(路径)中,查看本结点的左子节点和右子节点是否都是红色,若是,则将这个4-结点提前分裂以使得子树的结点可以有空间插入新key。插入完结点递归返回过程中再修正自顶向下分裂(⭐⭐如3-结点左键的左子结点是4-结点就会出现这种情况,正确的分裂应该是不断向上分裂,而现在由于查找的方向向下,每一层只分裂了一次就继续向下查找了,并没有继续往上分裂,所以会出现这个错误)和插入产生的连续红节点。

①、颜色转换(红黑树的分裂实现)

只需要将本结点的颜色改为红色,而将两子节点的颜色改为黑色即可

在这里插入图片描述

⭐⭐1、局部特性

**颜色转换这项操作最重要的性质在于它和旋转操作一样都是局部变换,不会影响整棵树的黑色平衡性。**可以看到,无论是旋转还是颜色变换,从它们的父节点到变换的子树经过的黑色结点并没有改变。

②、插入过程(将以下图示中的树看作是不完整的子树,W结点的右子树说明了这点,因为这些树并不符合上面所说的性质,只是为了演示过程而作的图,不要被我误导)

新key只有可能插入到2-结点、3-结点和4-结点(这里说的n-结点是在 向下递归查找操作 之前 的说法,当递归路径中存在4-结点就会提前分裂,真正执行插入时是不会插入进4-结点的,只可能插入进2、3-结点,特此说明)中,下面来讨论这三种情况。

1、2-结点插入

可以看到,插入2-结点并不会违背任何性质,直接插入即可。

2、3-结点左键插入

在向下递归查找路径中,3-结点并不会促使分裂,递归到目标位置可直接插入。只是插入完成后出现了连续红结点,需要在递归返回时做修复(fixUp())。

3、3-结点中键插入

同3-结点左键插入一样,要在递归返回时修复连续红结点。

4、3-结点插入小结(记)

3-结点可直接插入,不过要修复连续红结点。上面的修复操作看似无厘头,其实是有规律可循的。
在这里插入图片描述

其规律就是:3-结点中键插入的连续红节点情况可以转化为3-结点左键插入的情况,然后再对该子树进行右旋,就可以得到一个4-结点。要注意,此4-结点将会在下次插入中递归到该结点时分裂。

5、4-结点左键插入

在向下递归查找路径中,4-结点促使分裂,之后即可直接插入结点。

6、4-结点中键插入

同4-结点左键插入。

③、总结

和删除相比插入操作还是比较简单的,以开头所讲的思路为记忆框架,再配上插入过程应该就能理解插入操作了。

④、完整插入代码
    public void put(Key key, Value value) {
        root = put(root, key, value);
        //根节点链接为黑色
        root.color = BLACK;
    }

    private Node put(Node node, Key key, Value value) {
        if (node == null)   // 新建结点为红色
            return new Node(key, value, 1, RED);
        
  		if (isRed(node.left) && isRed(node.right)) flipColors(node);
        int cmp = key.compareTo(node.key);
        if (cmp < 0)       node.left = put(node.left, key, value);
        else if (cmp > 0)  node.right = put(node.right, key, value);
        else               node.value = value;

//-------------------------------------fixUp()-------------------------------------------
        if (!isRed(node.left) && isRed(node.right)) node = rotateLeft(node);
        if (isRed(node.left) && isRed(node.left.left)) node = rotateRight(node);
//-------------------------------------fixUp()-------------------------------------------     
        node.N = size(node.left) + size(node.right) + 1;
        return node;
    }
Ⅴ、自顶向下合并的删除操作

删除操作同样也分为向下递归查找、删除和递归返回fixUp()三个阶段。

总体的删除思路是:在向下递归查找目标key的过程中,若遇到2-结点,此时就通过对这个2-结点的 父亲结点 子树的旋转和变色操作(统称为合并),使得该2-结点变成3-结点或4-结点。在向下递归的路径中,通过这种 每次都将路径中下一个要递归的结点构造成3、4-结点的 变换,最终删除的结点就一定会是3、4-结点,这就保证了2-3-4-树的性质2。在找到目标结点后,若该结点有后继结点,则用后继结点的值替换目标结点的值,并以后继结点作为下一个 递归查找删除 的目标结点,重复上面所述的递归查找步骤(这里看不懂看下面的例子);若该目标结点是叶子节点,直接删除返回即可;若该结点没有后继结点但有左子树,这种情况则要另外分析。在真正删除掉结点后返回时,仍要像插入操作一样进行连续红结点的fixUp().

①、合并操作

可能出现的情况只有以下两种:

当递归路径中的2-结点的亲兄弟结点也为2-结点时,将该2-结点、父节点中链接左右结点的键和亲兄弟结点合并为一个4-结点,而使父结点的key数-1。

当递归路径中的2-结点的亲兄弟结点不为2-结点时,将父节点中链接左右结点的键和该2-结点合并,而将亲兄弟结点移动到父结点的左键中。

1、合并的意义

删除一个结点有可能会破坏红黑树的黑色平衡性,合并删除的意义就是使得 最终(递归删除的最后一个后继)移除的结点是一个红色结点(除了下面的一种特殊情况),这就保证了红黑树的性质5

②、删除过程

图中有完整解析,这里就不赘述了。

③、删除最终后继的特殊情况(该小节是个人思考观点,没有在网上或书上找到依据,请辨证观看)

下面来看一下删除最终后继的两种特殊情况

左边的情况可转换为右边的情况

④、总结

删除操作总的来说就三步:==合并向下递归路径中的2-结点、递归删除目标key的后继(实际上就是删除右子树的最小结点)、递归返回时向上fixUp(),以此为记忆框架就好。==其中删除完成之后 出现连续红结点的例子实在是没有找出来,这个fixUp()步骤是因为看见《算法》中有网上的博客也有我才加上去的,有时间再探讨一下。

主要参考资料

《算法》第四版

http://dandanlove.com/2018/03/18/red-black-tree/

https://www.cnblogs.com/nullzx/p/6111175.html

https://www.jianshu.com/p/5518d5740783

https://deserts.io/red-black-tree-deletion/

https://blog.csdn.net/leonliu1995/article/details/78374492

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值