【数据结构】红黑树的插入 —— 多图结合源码理解红黑树的插入

红黑树是一个特殊的二叉搜索弱平衡树,它是为了改善使用平衡树时需要大量维持平衡的操作二产生的。对于红黑树而言,实现的是黑节点的平衡,所以插入后继续维持红黑树的特性也较为方便。今天就来探讨红黑树的插入过程。

一、红黑树的规则:

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。
🔊红黑树的五个特征:

  1. 节点是红色或黑色。
  2. 根节点是黑色。
  3. 所有叶子都是黑色。(页子是NULL节点)
  4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长整棵树的黑节点时平衡的。当整个路径都是黑节点时,就是最短的路径,反之如果路径中时很色和红色节点交替,就是最长的路径。
当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质,这时候就需要进行树的调整,使之再次成为一个红黑树,以下篇幅就是介绍了当插入一个节点后,红黑树进行的调整。

二、红黑树的插入图解:

首先说明:插入的节点是红色节点
🔊在插入时,有两种情况:

  • 插入后没有破坏规则
  • 插入后破坏了规则

对于没有破坏的情况,就是插入节点的父节点是一个黑节点,这样由于没有破坏黑平衡,所以可以直接插入。
我们要考虑的就是会破坏规则的情况,也就是插入节点的父节点是一个红节点,这时后就要对其进行调整,使得重新满足红黑树规则4。

破坏规则的所有形态:

根据上面的规则,插入的节点都是红色。我们约定 cur作为插入的节点,parent作为插入节点的父节点,grandparent作为插入节点的父节点的父节点,uncle作为插入节点的父节点的兄弟节点。接下来看插入时会出现的8种样子:

1. 当 cur 的parent是grandparent的左孩子时:
在这里插入图片描述
2. 当 cur 的parent是grandparent的右孩子时:
在这里插入图片描述
总之,我们分类就是按照 parent节点是其父节点的左右节点和uncle节点的颜色


虽然,插入时有这8种形态,但总结下来的情况只有三种。接下来就来逐一学习这三种情况:

⭐情况 1:

先来将最简单的第一种:uncle节点为红 对应上面形态标号的 1,2,5,6
此时为了满足两个红节点不能连在一起的要求,就要进行调整,这种调整也很简单:
📌只需要把parentuncle的节点转黑,相应grandparent节点转红 , 调整后有可能会破坏上面数的规格,那就继续循环向上调整。

先来看图解:在这里插入图片描述

来讲下为什么可以这样调整:uncle节点为红,那么可以知道uncle如果还有孩子节点,那么孩子节点也只能都是黑节点,否则就不满足性质4了,所以为了达到黑平衡,就可以直接将grandparent的两个孩子都置为黑节点,同时将grandparent节点置为红。随着这个节点置为红,可能就会破坏和其父节点之间的规则,这时候就可以将其看作一个新的cur进行插入,一直按插入规则向上调整直到符合规格。

根据上面的调整原理可知道,只要这个节点的uncle是红色,那么都可以直接转换为情况1,也就是直接换颜色就好,所以上面的1,2,5,6 的情况都可以这样调整。

⭐情况 2 :

接下来是第二种情况:uncle节点为黑,且是左左型或者右右型,对应上面标号的情况 4,8
也就是这两种:
在这里插入图片描述
那么,对于这种情况怎么修复呢? 修复规则是:📌左左型对parent右旋,右右型对parent左旋,接着将parent置为黑色,grandparent置为红色
还是看图理解:

左左型:
在这里插入图片描述
右右型:
在这里插入图片描述

上面的图片是cur没有子树的变化过程,接着来看看有子树的情况,都是一样的规则:
在这里插入图片描述

这样调整之后,由于将parent置为黑色,所以不会继续破坏上面节点的规则,也就不需要向上调整了。
这里来解释下为啥上面图上看起来不满足规则五(就是黑节点数目一样)
对于grandparent节点来说:

  • 如果uncle节点为null,那 null 节点自身就是黑节点,而没插入 cur 之前 parent 也有为 null 的叶子节点,所以两个子树的黑节点是一样的。
  • 如果uncle不为空,uncle是有可能有子树的,那parent和cur也是有子树的,这种情况就来源于情况1中的由于调整一次后使得上面的树规则被破坏,因此还需要转化cur继续向上调整的情况。
⭐情况 3 :

最后一种情况就是uncle节点为黑,且是左右型或者右左型对应上面情况的3,7也就是这两种:
在这里插入图片描述
对于这种形状的修复就是:📌左右型对parent左旋,右左型对parent右旋,原来的curparent,原来的parentcur接着按照情况二修复
先来看图:
在这里插入图片描述
在这里插入图片描述

可以看出这种情况在进行这小小的调整后就可以转化为情况 2,这样就可以使用情况2 的修复规则进行修复。
所以我们是不是可以把情况2和情况3看成是一种情况的两个阶段呢?情况2 和 情况3 都可以看作是uncle节点为黑的情况,其中情况3 是第一阶段,情况2 是第二阶段,当判断到uncle节点为黑时,接着判断是否为情况3:如果是,那就处理到情况2的形态;如果直接是情况2,那就直接以情况2来修复。

最后要统一把根置为黑色。


🎈我们来总结下上面的情况:

  1. 如果parent为黑,直接插入
  2. 否则,如果uncle为红:uncle、parent变为黑色,grandparent变为黑色
  3. 否则,uncle为黑,判断是否左右或右左型
    (1)如果是,对parent进行左旋或右旋,交换parent和cur
    (2)对parent进行左旋或右旋,parent变为黑色,grandparent变为红色
  4. 将根节点置为黑色

三、红黑树的插入源码

对于代码的实现来说,还是遵守上面的情况,理解思路后,源码也比较容易看懂了。

展示源码的各部分对应的情况:
在这里插入图片描述
贴上源码:

private void fixAfterInsertion(Entry<K,V> x) {
        x.color = RED;

        while (x != null && x != root && x.parent.color == RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry<K,V> y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateRight(parentOf(parentOf(x)));
                }
            } else {
                Entry<K,V> y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == RED) {
                    setColor(parentOf(x), BLACK);
                    setColor(y, BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    x = parentOf(parentOf(x));
                } else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), BLACK);
                    setColor(parentOf(parentOf(x)), RED);
                    rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = BLACK;
    }

唠唠叨叨:
红黑树插入基本就是这样子了,看似很复杂其实分解归类后发现也不难,所以自己总结一次真的可以收获很多,也可以理清自己的思路,所以建议大家学完后自己进行总结下下。另外如果想更进一步到的了解红黑树,推荐一个博主的一篇文 红黑树,写的很棒相信看完后大家可以更加进一步理解红黑树的各种规则了。如果这篇文章右任何问题欢迎评论指正,也欢迎小伙伴们点赞关注一起进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
红黑树是一种自平衡的二叉查找树,它在插入和删除节点能够保持树的平衡。红黑树的概念可以参考。在Java中实现红黑树,可以按照以下步骤进行: 1. 首先将红黑树当作一颗二叉查找树,将新节点插入到适当的位置上。 2. 将插入的节点着色为"红色"。 3. 根据红黑树的特性,通过一系列的旋转和着色等操作,使树重新保持红黑树的性质。 具体的插入过程可以参考中提供的代码。在代码中,使用了左旋转、右旋转和颜色翻转等操作来重新平衡红黑树。 首先,如果节点的右子树是红色而左子树是黑色,可以通过左旋转操作将其变为左子树为红色,右子树为黑色的情况。 其次,如果节点的左子树和左子树的左子树都是红色,可以通过右旋转操作将其修正为上述情况。 最后,如果节点的左子树和右子树都是红色,可以通过颜色翻转操作将其修正为左子树和右子树都为黑色的情况。 在插入完节点后,需要将根节点的颜色设置为黑色,以确保红黑树的性质满足。 这样,通过以上的步骤,就能够实现对红黑树插入操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java数据结构红黑树的真正理解](https://download.csdn.net/download/weixin_38622475/12770272)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Java高阶数据结构红黑树](https://blog.csdn.net/qq15035899256/article/details/126678970)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [Java数据结构——红黑树](https://blog.csdn.net/weixin_30699463/article/details/95256212)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值