美文网首页一些收藏
《算法—深入浅出》红黑树的插入

《算法—深入浅出》红黑树的插入

作者: 青叶小小 | 来源:发表于2021-01-18 10:41 被阅读0次

一、《算法—深入浅出》N叉树的介绍
二、《算法—深入浅出》红黑树的旋转
三、《算法—深入浅出》红黑树的插入
四、《算法—深入浅出》红黑树的删除

一、前言

相信大家已经有了基本的节点旋转知识,本篇直接进入主题:新增节点。

新增节点的两条基础理论:
新增的节点一定是插入在叶子节点(除非该树为空);
新增的节点颜色一定先涂为红色;

第1点很好理解,因为 RBT 也是 BST 树,因此,通过二分法查找到叶子节点后,再插入新的节点;
\color{red}{至于第2点,为什么新增的节点为红色?}
这是需要满足红黑树第5点性质(任一节点到叶子节点,所有路径上的黑色节点数都要相同),因此,新增红色节点不会改变这条性质。

二、插入

A)根据上面的基础理论,插入也就可以划分为:

  • 新增节点在左子树的左叶子节点;
  • 新增节点在左子树的右叶子节点;
  • 新增节点在右子树的左叶子节点;
  • 新增节点在右子树的右叶子节点;

B)同时考虑因为新增节点是红色节点:

  • 如果其父节点是黑色,那么结束,不需要调整;
  • 如果其父节点是红色,那么就违反了 RBT 第4点,上下相邻不能有连续的红色节点;

因此,我们只需要考虑(A)中四种情况 +(B)中第2种情况即可。

在调整红黑树时,我们不能只看父节点,还需要看其叔叔节点(父与叔都是其祖父的儿子节点):


示意.png

如上图:

  • 未插入 14 节点前,有可能的状态:祖父黑色、父红色、叔叔黑色;
  • 插入 14 节点后,就违背第 4 点;

但如果仅仅只是改变『父节点』的颜色,可能就违背第 5 点(所有路径黑色节点数相同);

之所有存在这种情况,是因为之前 RBT 已经满足 5 条要求,但『父节点』与『叔叔节点』颜色不一致。

可以查看《算法—深入浅出》N叉树的介绍中红黑树的例子。

所以,插入节点后,是否需要调整 RBT 要看父节点与叔叔节点这两个节点的颜色,再考虑所在的子树的左或右来旋转。

2.1、左子树 + 新增节点

  1. 如果父节点为黑色,则本次结束;

\color{red}{While循环}:当父节点存在 && 父节点为红色(祖父节点必为黑色,RBT 第4点):

  1. 如果叔叔节点为红色,那么;
  • 改变父节点和叔叔节点为黑色,祖父节点为红色;
  • 将当前节点指向祖父节点;
  • 继续;
  1. 如果新增节点为父节点的右节点:
  • 左旋父节点;
  • 互换父节点与新增节点的指针;
  1. 如果叔叔节点为黑色(叔叔节点不存在也是黑色,RBT 第3点 叶子NIL节点是黑色),则:
  • 改变父节点为黑色,祖父节点为红色;
  • 右旋祖父节点;
  • 继续;

针对上面的2、3、4,我们用图来说明:

  • 第4点情况示意图:


    4.png
  • 第3点情况示意图:


    3.png
  • 第2点情况示意图:


    2.png

请注意,上面三个示意图中,大家请仔细的看看:

  • 4.a
  • 3.c
  • 2.c

你会发现,第2、3点的情况,最终都会转化为第4点的初始状态!我把上面三张图再合成为一张大图大家看看:


左+新.png

2.2、右子树 + 新增节点

我们分析完了『左子树 + 新增节点』,那么『右子树 + 新增节点』实际上就是对其条件的左、右互换,以及旋转互换!

2.3、完整的图例

红黑树插入.png

三、算法实现

public class RBTree {
    /*****************************************************************************
     * 插入新结点
     *****************************************************************************/
    public void insert(TreeNode cur) {
        TreeNode p = root;
        TreeNode g = p;
        while (p != null) {
            g = p;
            if (cur.value < p.value) {
                p = p.left;
            } else {
                p = p.right;
            }
        }
    
        if (g == null) {
            root = cur;
        } else if (cur.value < g.value) {
            g.left = cur;
        } else {
            g.right = cur;
        }
        cur.parent = g;
        cur.color = TreeNode.RED;
    
        // 插入完后,修正红黑树
        // 可能违背:
        // 第4点:如果一个节点是红色的,则它的子节点必须是黑色的
        // 第2点:新插入结点就是根节点,因此,需要着色为黑色
        fixedInsert(cur);
    }
    
    /*****************************************************************************
     *   g: 祖父节点
     *   u:叔叔节点
     *   p:父节点
     * cur:新插入节点
     *****************************************************************************/
    private void fixedInsert(TreeNode cur) {
        TreeNode g, u;
        TreeNode p;
        while ((p = cur.parent) != null && p.color == TreeNode.RED) {
            g = p.parent;
    
            /******************************************************
             * 【左、右】算法互为镜像
             ******************************************************/
            if (g != null) {
    
                /*************************************************
                 * 叔叔节点且红色:
                 * 1. 修改:p & u  为黑色;
                 * 2. 修改:g      为红色;
                 *************************************************/
                u = (p == g.left ? g.right : g.left);
                if (u != null && u.color == TreeNode.RED) {
                    u.color = TreeNode.BLACK;
                    p.color = TreeNode.BLACK;
                    g.color = TreeNode.RED;
                    cur = g;
                    continue;
                }
    
                /**************************************************
                 * 左子树
                 **************************************************/
                if (p == g.left) {
                    /*********************************************
                     *     g            g            g
                     *    /            /            /
                     *   p     =>    cur     =>    p
                     *    \          /            /
                     *     cur      p            cur
                     *********************************************/
                    if (cur == p.right) {
                        rotateLeft(p);
                        TreeNode temp = cur;
                        cur = p;
                        p = temp;
                    }
    
                    /*********************************************
                     * 1. 叔叔节点为黑色
                     * 2. 叔叔节点不存在,即叶子结点NIL也是黑色
                     *
                     *       g          p
                     *      /          / \
                     *     p   =>    cur  g
                     *    /
                     *  cur
                     *********************************************/
                    p.color = TreeNode.BLACK;
                    g.color = TreeNode.RED;
                    rotateRight(g);
    
                } else {
                    /*********************************************
                     *     g             g            g
                     *      \            \            \
                     *       p     =>    cur     =>    p
                     *      /             \            \
                     *     cur             p           cur
                     *********************************************/
                    if (cur == p.left) {
                        rotateRight(p);
                        TreeNode temp = cur;
                        cur = p;
                        p = temp;
                    }
    
                    /*********************************************
                     * 1. 叔叔节点为黑色
                     * 2. 叔叔节点不存在,即叶子结点NIL也是黑色
                     *
                     *     g                p
                     *     \               / \
                     *     p       =>     g  cur
                     *      \
                     *      cur
                     *********************************************/
                    p.color = TreeNode.BLACK;
                    g.color = TreeNode.RED;
                    rotateLeft(g);
                }
            }
        }
    
        // 最后,必需确保满足根节点必需是黑色
        // 因为我们的调整,到根节点(当 g 点为 null 时,表明 parent 已经为树根)
        root.color = TreeNode.BLACK;
    }
}

相关文章

  • 红黑树核心之节点新增

    红黑树插入算法 红黑树节点插入与二叉搜索树类似,由根节点开始寻找待插入的位置。与二叉搜索树不同的内容大致有如下几点...

  • 《算法—深入浅出》红黑树的插入

    一、《算法—深入浅出》N叉树的介绍[https://www.jianshu.com/p/3b9e7267b776]...

  • 红黑树专题

    0.目录 1.算法导论的红黑树本质上是2-3-4树 2.红黑树的结构和性质 3.红黑树的插入 4.红黑树的删除 5...

  • 数据结构—树—红黑树

    红黑树概述 红黑树的插入 红黑树的删除

  • 红黑树的插入算法

    红黑树是平衡二叉查找树 (Balanced BST),和普通的二叉查找树相比,红黑树的节点中还存有节点的颜色(红或...

  • 彻底理解红黑树(二)之 插入

    彻底理解红黑树(一)之 二叉搜索树彻底理解红黑树(二)之 插入彻底理解红黑树(三)之 删除 前言 红黑树的插入情况...

  • [转载]红黑树

    https://zhuanlan.zhihu.com/p/24367771红黑树简介红黑树插入红黑树删除

  • 数据结构与算法-AVL 红黑树

    AVL树AVL树 算法红黑树红黑树 B站

  • HashMap 1.8 较 1.7 的改变

    核心变化 hash 算法优化 链表插入改为尾插法 引入红黑树 hash 算法优化 旨在提升hash计算性能 JDK...

  • 无标题文章

    ## 知识点 1. 算法 基本的排序算法,时间复杂度 2. 数据结构 链表,查找,插入;平衡二插树,红黑树 2. ...

网友评论

    本文标题:《算法—深入浅出》红黑树的插入

    本文链接:https://www.haomeiwen.com/subject/xmoaaktx.html