红黑树

作者: 某昆 | 来源:发表于2017-07-03 19:23 被阅读135次

1.前言

一棵高度为h的二叉搜索树,基本操作时间复杂度为O(h),当二叉搜索树高度较低时,效率较高,如果二叉搜索树高度较高则效率较低了,极限情况下,向二叉搜索树顺序插入一个数组,那么二叉搜索树的效率为O(n)。

红黑树也是一种二叉搜索树,它在每一个结点上添加了颜色,颜色可以是红或黑。通过相关规则约束,红黑树确保没有一条路径会比其它路径长出2倍,因此红黑树是近似平衡的,不会出现前文所说的极限情况。

红黑树必须满足以下五个条件:

  • 每个结点颜色是红色或者是黑色
  • 根结点是黑色的
  • 每个叶结点(指空结点,非叶子结点)是黑色的
  • 如果一个结点是红色的,那么它的两个子结点都是黑色的
  • 对每一个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点

一棵典型的红黑树:

rbtree.jpg

为什么红黑树没有一条路径会比其它路径长两倍呢?

红黑树中最短的路径,一定是全黑结点,最长的路径一定是黑红相间节点,要保持性质5,所以最长路径是最短路径的2倍

2、红黑树插入

红黑树的插入和二叉搜索树插入类似,根据结点值的大小,确定它的位置。

  public void rb_insert(RBNode z){
    RBNode y = null;
    RBNode x = mRoot;
    while (x != null) {
        y = x;
        if (z.value < x.value) {
            x = x.left;
        }else {
            x = x.right;
        }
    }
    z.parent = y;
    if (y == null) {
        mRoot = z;
    }else if (z.value < y.value) {
        y.left = z;
    }else {
        y.right = z;
    }
    z.left = null;
    z.right = null;
    z.color = RED;
    rb_insert_fixup(z);
}

由于新插入节点可能破坏红黑树的性质, 所以插入最后必须进行红黑树性质的修复。

为了尽量少违背红黑树的性质,设定新插入节点的颜色均为红色

由于新插入节点为红色,可能出现以下几种情况:

  • 新插入节点为根节点,直接置黑即可
  • 新插入节点的父节点为黑色,不违背任何规则
  • 新插入节点的父节点为红色,违背规则4

如果违背规则4,那么也有以下三种情况出现(新插入节点为z)

1、父节点及叔叔节点均为红色,祖父节点为黑色(根据红黑树性质,未插入前没有任何规则被破坏,那么祖父节点不可能为红色)

Paste_Image.png

此时,规则4被破坏,则规则5是正常的,只需将父节点、叔叔节点置为黑色,祖父节点置为红色,此当前子树完成平衡。采取递归思想,将整棵子树当成一个新的红色节点向上循环处理,设置z等于祖父节点

2、z的叔叔节点为黑色且z是右子节点

Paste_Image.png

红黑树插入修复的终极思想是,把多余的红色节点向根节点方向移动,变成根节点

根据插入修复思想,目前z是一个多余的红色节点,且此时节点不平衡,为了让z向根节点方向移动,则以z的父节点左旋转即可

3、z的叔叔节点为黑色且z是左子节点

Paste_Image.png

此时,以z的祖父节点右旋转,同时将z的父节点设置为黑色,祖父节点设置为红色,此时,红黑树平衡了。

仔细观察,其实情况2这么做,也是为了满足情况3的初始条件,以便旋转后,达到新的平衡。

//添加修复核心思想为,将多作的红色节点向根节点移动,最后把根节点弄成黑色即可
public void rb_insert_fixup(RBNode z){
    RBNode y = null;
    while (z.parent != null && z.parent.color == RED) {
        if (z.parent == z.parent.parent.left) {
            y = z.parent.parent.right;
            //z的父节点为红,叔叔节点也为红
            if (y.color == RED) {
                z.parent.color = BLACK;
                y.color = BLACK;
                z.parent.parent.color = RED;
                z = z.parent.parent;
            }else if (z == z.parent.right) {
                //叔叔节点为黑,且z是z的父亲的右子节点
                z = z.parent;
                leftRotate(z);
            }else {
                //叔叔节点为黑,且z是z的父亲的左子节点
                z.parent.color = BLACK;
                z.parent.parent.color = RED;
                rightRotate(z.parent.parent);
            }
        }else {
            y = z.parent.parent.left;
            if (y.color == RED) {
                z.parent.color = BLACK;
                y.color = BLACK;
                z.parent.parent.color = RED;
                z = z.parent.parent;
            }else if (z == z.parent.left) {
                //叔叔节点为黑,且z是z的父亲的右子节点
                z = z.parent;
                rightRotate(z);
            }else {
                //叔叔节点为黑,且z是z的父亲的左子节点
                z.parent.color = BLACK;
                z.parent.parent.color = RED;
                leftRotate(z.parent.parent);
            }
        }
    }
    mRoot.color = BLACK;
}

3、红黑树删除

红黑树的删除比插入更复杂,情况更多,但思想是一致的。类似二叉搜索树,找节点删除,维持二叉搜索树的性质,再修复红黑树的性质

  public void rb_delete(RBNode z){
    RBNode y = z;
    RBNode x = null;
    int yOriginColor = y.color;
    if (z.left == null) {
        x = z.right;
        rb_transplant(z, z.right);
    }else if (z.right == null) {
        x = z.left;
        rb_transplant(z, z.left);
    }else {
        y = treeMin(z.right);
        yOriginColor = y.color;
        x = y.right;
        if (y.parent == z) {
            x.parent = y;
        }else {
            rb_transplant(y, y.right);
            y.right = z.right;
            y.right.parent = y;
        }
        rb_transplant(z, y);
        y.left = z.left;
        y.left.parent = y;
        y.color = z.color;
    }
    if (yOriginColor == BLACK) {
        rb_delete_fixup(x);
    }
}

删除节点的关键是:

  • 如果删除的是红色节点,那么不会破坏性质
  • 如果删除的是黑色节点,那么,这个路径上会少一个黑色节点,性质5被破坏。所以删除算法就是通过重新着色、旋转,来恢复性质5

1、节点n为黑色,兄弟节点s也为黑色,s的子节点也为黑色

Paste_Image.png

将s节点设置为红色,当前子树已经恢复平衡,但由于删除了一个黑色节点,整体仍未平衡。以递归思想处理,将n赋值为p,以整个子树为新的n

2、承接上文,s节点为红,且s的子节点都为黑

Paste_Image.png

将s和p节点颜色互换,同时在p节点左旋

3、s左节点为红,自身为黑时

Paste_Image.png

交换s和sl颜色,同时以s中心右旋转

4、s右节点为红,自向为黑

Paste_Image.png

将s颜色置为s父节点的颜色,s父节点颜色置黑,右节点颜色置黑,同时以s父节点左旋转

//删除修复的核心思想是,x代替了y原来的位置,如果y是黑的,y位置移动了,那么规则5可能被破坏
//如果想象x有两个颜色,自身加一个黑色,替补y的黑色,那规则5则不再破坏
//如果x自身为红色加额外黑色,则此时所有规则都ok
//如果x自身为黑,额外也是黑,那么规则5则被破坏
//现在则要想办法将x弄成红色+黑色的模式,将多余黑色向根节点移动
public void rb_delete_fixup(RBNode x){
    if (x == null) {
        return;
    }
    RBNode w = null;
    while (x != mRoot && x.color == BLACK) {
        if (x == x.parent.left) {
            w = x.parent.right;
            if (w.color == RED) {
                w.color = BLACK;
                x.parent.color = RED;
                leftRotate(x.parent);
                w = x.parent.right;
            }else if ((w.left == null || w.left.color == BLACK)
                    && (w.right == null || w.right.color == BLACK)) {
                w.color = RED;
                x = x.parent;
            }else if (w.right == null || w.right.color == BLACK) {
                w.left.color = BLACK;
                w.color = RED;
                rightRotate(w);
                w = x.parent.right;
            }else {
                w.color = x.parent.color;
                x.parent.color = BLACK;
                w.right.color = BLACK;
                leftRotate(x.parent);
                x = mRoot;
            }
        }else {
            w = x.parent.left;
            if (w.color == RED) {
                w.color = BLACK;
                x.parent.color = RED;
                rightRotate(x.parent);
                w = x.parent.left;
            }else if ((w.left == null || w.left.color == BLACK) 
                    && (w.right == null || w.right.color == BLACK)) {
                w.color = RED;
                x = x.parent;
            }else if (w.left == null || w.left.color == BLACK) {
                w.right.color = BLACK;
                w.color = RED;
                leftRotate(w);
                w = x.parent.left;
            }else {
                w.color = x.parent.color;
                x.parent.color = BLACK;
                w.left.color = BLACK;
                rightRotate(x.parent);
                x = mRoot;
            }
        }
    }
    x.color = BLACK;
}    

以上即是红黑树删除操作,在算法导论上,红黑树删除思想是,设置N节点有两重颜色,自已本身颜色加上额外黑色,用以补偿被删除的黑色节点,然后想办法将N的颜色置为红色加黑色或者是根节点,然后置为黑色,则整个红黑树重新平衡。但以上四种情况也是一样的

红黑树的插入删除操作都挺复杂的,本人自己也有很多不理解的,现在记录如上,相信随意时间推移,会有更多新的理解。

以上所有代码均上传到本人github,欢迎访问。

相关文章

  • 数据结构—树—红黑树

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

  • TreeMap

    需要先了解红黑树,这是之前分析红黑树的文章。之前在分析红黑树时,我认为红黑树=二叉查找树+红黑平衡,关于二叉查找树...

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

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

  • [转载]红黑树

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

  • 拿下红黑树

    红黑树 红黑树、2-3树的简单定义: 实现红黑树的基本结构以及添加操作(维护定义,左旋、右旋、颜色反转) 红黑树与...

  • 红黑树

    啥是红黑树,红黑树 = 二叉树

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

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

  • 彻底理解红黑树(三)之 删除

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

  • Golang红黑树

    红黑树 红黑树是每个节点都带有颜色属性(红色或黑色)的二叉查找树。红黑树也属于自平衡二叉查找树。 红黑树具有如下性...

  • 数据结构红黑树添加、修改原理分析

    源码分析大纲 数据结构解析 红黑树试下原理刨析 数据结构解析 1.红黑树 1.1 红黑树概念 红黑树(Red Bl...

网友评论

    本文标题:红黑树

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