数据结构08-红黑树

原创 2018年04月15日 23:24:03

数据结构08-红黑树

一、红黑树的介绍

红黑树(RBT)是每个节点都带有颜色属性的自平衡二叉查找树,颜色或红色或黑色。具备以下性质:

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

由于AVL的树增删效率很低,所以出现了红黑树。红黑树的查找效率比一般的二叉查找树高,比AVL树低,但是增删效率比AVL树高,比一般的二叉查找树低。

红黑树的应用:TreeMap、TreeSet

二、红黑树的插入

红黑树的插入分为两步:添加、修正平衡。

这里把新增节点记作A,把A的父节点记作B,把找到的最小不平衡子树记作C。

1、添加

第一步的添加跟二叉搜索树的插入一样。

2、修正平衡

将当前节点记作A,将A的父节点记作B,将B的兄弟节点记作C,将B的父结点记作D。

修正过程如下:

  1. 将A设为红色,这样对整颗树的平衡影响较小;
  2. 如果A为根节点,则将根节点改成黑色即可,修正结束;
  3. 如果B是黑色,那么树就是平衡的,修正结束;
  4. 如果B是红色,并且B是D的左孩子,则判断C的颜色:
    • 如果C是红色,则将B和C涂黑,D涂红,把当前节点(A)指向D,然后继续比较A;
    • 如果C是黑色,并且A是B的左孩子,则把B涂黑,D涂红,然后对D右旋,然后继续比较A;
    • 如果C是黑色,并且A是B的右孩子,则把当前节点(A)指向B,然后对新的A左旋。然后把B指向新A的父亲,把D指向新B的父亲,然后把B涂黑,D涂红,对D右旋,然后继续比较A。
  5. 如果B是红色,并且B是D的右孩子,则判断C的颜色:
    • 如果C是红色,则将B和C涂黑,D涂红,把当前节点(A)指向D,然后继续比较A;
    • 如果C是黑色,并且A是B的右孩子,则把B涂黑,D涂红,然后对D左旋,然后继续比较A;
    • 如果C是黑色,并且A是B的左孩子,则把当前节点(A)指向B,然后对新的A右旋。然后把B指向新A的父亲,把D指向新B的父亲,然后把B涂黑,D涂红,对D左旋,然后继续比较A。

添加元素:

public void put(E data) {
    Node<E> now = new Node<E>(data);
    if (root == null) {
        root = now;
        return;
    }

    Node<E> parent = root;
    // 像二叉查找树一样添加
    while (parent != null) {
        if (data.compareTo(parent.data) < 0) {
            if (parent.left == null) {
                parent.left = now;
                now.parent = parent;
                break;
            } else {
                parent = parent.left;
            }
        } else if (data.compareTo(parent.data) > 0) {
            if (parent.right == null) {
                parent.right = now;
                now.parent = parent;
                break;
            } else {
                parent = parent.right;
            }
        } else {
            return;
        }
    }
    //修正位置
    fixAfterInsertion(now);
}

修正位置:

private void fixAfterInsertion(Node<E> node) {
    node.color = RED;

    Node<E> parent = null;
    Node<E> brother = null;
    while (node != null && node != root && node.parent.color == RED) {
        parent = node.parent;
        if (parent.parent != null && parent.parent.left == parent) {
            brother = parent.parent.right;
            if (getColor(brother) == RED) {
                setColor(parent, BLACK);
                setColor(brother, BLACK);
                setColor(parent.parent, RED);
                node = parent.parent;
            } else {
                if (parent.right == node) {
                    node = parent;
                    leftRoate(node);
                }
                parent = node.parent;
                setColor(parent, BLACK);
                setColor(parent.parent, RED);
                rightRoate(parent.parent);
            }
        } else {
            brother = parent.parent == null ? null : parent.parent.left;
            if (getColor(brother) == RED) {
                setColor(parent, BLACK);
                setColor(brother, BLACK);
                setColor(parent.parent, RED);
                node = parent.parent;
            } else {
                if (parent.left == node) {
                    node = parent;
                    rightRoate(node);
                }
                parent = node.parent;
                setColor(parent, BLACK);
                setColor(parent.parent, RED);
                leftRoate(parent.parent);
            }
        }
    }

    root.color = BLACK;
}

三、红黑树的删除

红黑树的插入分为两步:删除、修正平衡。

这里把需要修正的节点记作A,把A的父节点记作B,把找到的最小不平衡子树记作C。

1、删除

第一步的删除跟二叉搜索树的插入一样,只是要将被删除节点的替代节点当作需要修正的结点。

注意:如果被删结点是红色,那么不影响树的平衡,不需要修正;

2、修正平衡

将当前节点记作A,将A的父节点记作B,将A的兄弟节点记作C,将B的父结点记作D。

修正过程如下:

  1. 如果A为根节点,则将A设为黑色,修正结束;
  2. 如果A为红色,则将A设为黑色,修正结束;
  3. 如果A为B的左子节点,则

    • 先判断C的颜色,如果C为红色,则将C设为黑色,将B设为红色,然后对B左旋,然后将B指向新A的父亲,将C指向新B的兄弟
    • 然后判断C的左右子节点是否都为黑色,如果都为黑色,则将C设为红色,将A指向B,然后继续判断A;
    • 如果C的右子节点是黑色,C的左子节点是红色或黑色,则先将C的左子节点设为黑色,将C设为红色,对B右旋,将B指向新A的父亲,将C指向新B的兄弟;然后将C设为B的颜色,将B设为黑色,将B的右子节点设为黑色,再将B左旋,将A指向root节点。
    • 如果C的右子节点是红色,C的左子节点是红色或黑色,则将C设为B的颜色,将B设为黑色,将B的右子节点设为黑色,再将B左旋,将A指向root节点。
  4. 如果A为B的右子节点,则

    • 先判断C的颜色,如果C为红色,则将C设为黑色,将B设为红色,然后对B右旋,然后将B指向新A的父亲,将C指向新B的兄弟
    • 然后判断C的左右子节点是否都为黑色,如果都为黑色,则将C设为红色,将A指向B,然后继续判断A;
    • 如果C的左子节点是黑色,C的右子节点是红色,则先将C的右子节点设为黑色,将C设为红色,对B左旋,将B指向新A的父亲,将C指向新B的兄弟;然后将C设为B的颜色,将B设为黑色,将B的左子节点设为黑色,再将B右旋,将A指向root节点。
    • 如果C的左子节点是红色,C的右子节点是红色或黑色,则将C设为B的颜色,将B设为黑色,将B的左子节点设为黑色,再将B右旋,将A指向root节点。

删除:

public Node<E> remove(E data) {
    Node<E> now = get(data);
    if (now == null) {
        throw new NoSuchElementException();
    }

    Node<E> p = get(data);
    if (p == null) {
        throw new NoSuchElementException();
    }
    //p的右子树的最左子节点r
    Node<E> r = nodeLeft(p.right);
    //p的左子树的最右子节点l
    Node<E> l = nodeRight(p.left);
    if (r != null) {
        p.data = r.data;
        //如果p的右子结点有左节点
        if (r != p.right) {
            r.parent.left = r.right;
        } else {
            p.right = p.right.right;
        }
        //如果被删节点是黑色,就对替换的节点进行修正,即从平衡破坏的地方开始向上修正
        if (now.color == BLACK) {
            fixAfterDelete(r.right);
        }
        r.left = r.right = r.parent = null;

    } else if (l != null) {
        p.data = l.data;
        //如果p的左子结点有右节点
        if (l != p.left) {
            l.parent.right = l.left;
        } else {
            p.left = p.left.left;
        }
        //如果被删节点是黑色,就对替换的节点进行修正,即从平衡破坏的地方开始向上修正
        if (now.color == BLACK) {
            fixAfterDelete(l.left);
        }
        l.left = l.right = l.parent = null;

        //如果p是叶子节点
    } else {
        //如果p是叶子节点,但不是根节点,且颜色为黑色就需要对其修正
        if (p.parent != null && p.color == BLACK) {
            fixAfterDelete(p);
        }

        if (p.parent == null) {
            root = null;
        } else if (p.parent.left == p) {
            p.parent.left = null;
        } else {
            p.parent.right = null;
        }
        p.parent = null;
    }
    return now;
}

修正位置:

private void fixAfterDelete(Node<E> node) {
    if (node == null) {
        return;
    }

    Node<E> parent = null;
    Node<E> brother = null;
    while (node != root && getColor(node) == BLACK) {
        parent = node.parent;
        if (node == parent.left) {
            brother = parent.right;

            if (getColor(brother) == RED) {
                setColor(brother, BLACK);
                setColor(parent, RED);
                leftRoate(parent);
                parent = node.parent;
                brother = parent.right;
            }

            if (getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
                setColor(brother, RED);
                node = node.parent;
            } else {
                if (getColor(brother.right) == BLACK) {
                    setColor(brother.left, BLACK);
                    setColor(brother, RED);
                    rightRoate(brother);
                    parent = node.parent;
                    brother = parent.right;
                }
                setColor(brother, getColor(parent));
                setColor(parent, BLACK);
                setColor(brother.right, BLACK);
                leftRoate(parent);
                node = root;
            }
        } else { // symmetric
            brother = parent.left;

            if (getColor(brother) == RED) {
                setColor(brother, BLACK);
                setColor(parent, RED);
                rightRoate(parent);
                parent = node.parent;
                brother = parent.left;
            }

            if (getColor(brother.left) == BLACK && getColor(brother.right) == BLACK) {
                setColor(brother, RED);
                node = node.parent;
            } else {
                if (getColor(brother.left) == BLACK) {
                    setColor(brother.right, BLACK);
                    setColor(brother, RED);
                    leftRoate(brother);
                    parent = node.parent;
                    brother = parent.left;
                }
                setColor(brother, getColor(parent));
                setColor(parent, BLACK);
                setColor(brother.left, BLACK);
                rightRoate(parent);
                node = root;
            }
        }
    }

    setColor(node, BLACK);
}

demo已上传gitee,需要的同学可以下载!

上一篇:数据结构07-AVL树

下一篇:数据结构09-图

书友会主讲:张永强

  • foxfly
  • foxfly
  • 2004-04-17 10:42:00
  • 638

Java数据结构----树--红黑树

暂时没实现
  • oChangWen
  • oChangWen
  • 2016-02-28 10:49:44
  • 1046

红黑树理解 - 数据结构

红黑树        红黑树是很多平衡树的一种,保证最坏情况下基本动态几何操作时间复杂度为O(log(n))   1、红黑树性质 (1)   每个节点是红色的,或者是黑色的 (2)   根节点是黑色的...
  • u012796139
  • u012796139
  • 2015-05-07 18:31:21
  • 1051

重温数据结构:深入理解红黑树

读完本文你将了解到: 什么是红黑树黑色高度 红黑树的 5 个特性 红黑树的左旋右旋 指定节点 x 的左旋 右图转成左图 指定节点 y 的右旋左图转成右图 红黑树的平衡插入 二叉查找树的插入 插入后调整...
  • u011240877
  • u011240877
  • 2016-11-25 00:58:51
  • 12082

【数据结构和算法05】 红-黑树(看完包懂~)

从第4节的分析中可以看出,二叉搜索树是个很好的数据结构,可以快速地找到一个给定关键字的数据项,并且可以快速地插入和删除数据项。但是二叉搜索树有个很麻烦的问题,如果树中插入的是随机数据,则执行效果很好,...
  • eson_15
  • eson_15
  • 2016-04-13 15:50:25
  • 33213

Java数据结构与算法解析(十一)——红黑树

前面一篇文章介绍了2-3查找树,2-3查找树能保证在插入元素之后能保持树的平衡状态,最坏情况下即所有的子节点都是2-node,树的高度为lgN,从而保证了最坏情况下的时间复杂度。但是2-3树实现起来比...
  • u012124438
  • u012124438
  • 2017-10-11 09:46:24
  • 9079

数据结构——红黑树(RB-Tree)

红黑树本质上是一棵二叉查找树,但在二叉查找树的基础上,每个节点增加了一位存储来表示节点的颜色。有关二叉查找树的介绍在前面博文已经介绍过了,这里不再进行讲解。 红黑树的性质: 1)每个结点要么是红的,...
  • chenhanzhun
  • chenhanzhun
  • 2014-08-06 18:36:07
  • 2273

数据结构_红黑树

今年的第一篇博客,距离上一篇博客竟然已经一个月了。最近都静不下心来学习 ,一则刚过完年,二则刚换工作。好了,不扯了,转入正题吧。 今天讨论的是红黑树,红黑树有一个特点:保证了树结构是基本...
  • u011638883
  • u011638883
  • 2014-02-25 19:09:21
  • 934

数据结构之重要树总结(红黑树、B/B+树等)

众所周知,二叉树在数据结构中的分量举足轻重。之所以分量如此重,是因为在实际中有很多情况用此数据结构会产生很多好处。本文主要对二叉搜索树、平衡二叉树、红黑树、B(B+、B*)树进行总结。...
  • cj7749910
  • cj7749910
  • 2014-08-16 14:38:17
  • 1506

【C++研发面试笔记】11. 基本数据结构-红黑树RBT

【C++研发面试笔记】11. 基本数据结构-红黑树RBT上一节,我们提到了为了解决二叉查找树不平衡问题,我们引入了AVL树,AVL是严格平衡树,但在增加或删除节点时,需要非常多的旋转操作。因此这一节我...
  • tostq
  • tostq
  • 2016-10-03 12:09:57
  • 927
收藏助手
不良信息举报
您举报文章:数据结构08-红黑树
举报原因:
原因补充:

(最多只允许输入30个字)