数据结构:红黑树

一、定义

    红黑树是一种特殊的二叉查找树,它的每一个结点都被标记为红色或者黑色。是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。它是在1972年由Rudolf Bayer发明的,他称之为"对称二叉B树",它现代的名字是在 Leo J. Guibas 和 Robert Sedgewick 于1978年写的一篇论文中获得的。它是复杂的,但它的操作有着良好的最坏情况运行时间,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n 是树中元素的数目。在C++ STL中,很多部分(目前包括set, multiset, map, multimap)应用了红黑树的变体。

二、性质

    将一棵标准二叉树的所有空指针用外部结点替代,就生成了一棵扩展二叉树。红黑树以扩展二叉树为基础,又加入了以下性质:

  • 根结点和所有外部结点都是黑色;
  • 所有从根结点至外部结点的路径上,不能包含两个连续的红色结点;
  • 所有从根结点至外部结点的路径上,包含相同数目的黑色结点。

    我们可以把所有指向黑色结点标记为黑色,把所有指向红色结点的指针标记为红色,从而获得红黑树的一种等价的定义:

  • 从内部结点指向外部结点的指针都为黑色;
  • 所有从根结点到外部结点的路径上,不能包含两个连续的红色指针;
  • 所有从根结点到外部结点的路径上,包含相同数目的黑色指针。

        我们可以根据指针的颜色推断出结点的颜色,反之亦然。这些约束强制了红黑树的关键性质: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。结果是这个树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树。

三、操作

    因为每一个红黑树也是一个特化的二叉搜索树,因此红黑树上的只读操作与普通二叉搜索树上的只读操作相同。然而,在红黑树上进行插入操作和删除操作会导致不再符合红黑树的性质。恢复红黑树的属性需要少量(O(log n))的颜色变更(实际是非常快速的)和不超过三次树旋转(对于插入操作是两次)。虽然插入和删除很复杂,但操作时间仍可以保持为 O(log n) 。

1、插入

    我们首先以二叉搜索树的方法增加节点并标记它为红色。(如果设为黑色,就会导致根到叶子的路径上有一条路上,多一个额外的黑节点,这个是很难调整的。但是设为红色节点后,可能会导致出现两个连续红色节点的冲突,那么可以通过颜色调换(color flips)和树旋转来调整。) 下面要进行什么操作取决于其他临近节点的颜色。同人类的家族树中一样,我们将使用术语叔父节点来指一个节点的父节点的兄弟节点。注意:

    性质1 节点是红色或黑色。 所有叶子都是黑色。总是保持着。
    性质2 每个红色节点的两个子节点都是黑色。只在增加红色节点、重绘黑色节点为红色,或做旋转时受到威胁。
    性质3 从每个叶子到根的所有路径都包含相同数目的黑色节点。只在增加黑色节点、重绘红色节点为黑色,或做旋转时受到威胁。

    在下面的示意图中,将要插入的节点标为NN的父节点标为PN的祖父节点标为GN的叔父节点标为U。在图中展示的任何颜色要么是由它所处情形所作的假定,要么是这些假定所暗含的。

情形1: 新节点N位于树的根上,没有父节点。在这种情形下,我们把它重绘为黑色以满足性质1 根是黑色。因为它在每个路径上对黑节点数目增加一,性质3符合。

情形2: 新节点的父节点P是黑色,所以性质2没有失效(新节点是红色的)。在这种情形下,树仍是有效的。性质3受到威胁,因为新节点N有两个黑色叶子儿子;但是由于新节点N是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点,所以这个性质依然满足。

    注意: 在下列情形下我们假定新节点有祖父节点,因为父节点是红色;并且如果它是根,它就应当是黑色。所以新节点总有一个叔父节点,尽管在情形4和5下它可能是叶子。
情形3: 如果父节点P和叔父节点U二者都是红色,则我们可以将它们两个重绘为黑色并重绘祖父节点G为红色(用来保持性质3)。现在我们的新节点N有了一个黑色的父节点P。因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G,在这些路径上的黑节点数目没有改变。但是,红色的祖父节点G的父节点也有可能是红色的,这就违反了性质2。为了解决这个问题,我们在祖父节点G上递归地进行情形1的整个过程。
情况 3 示意图

 注意: 在余下的情形下,我们假定父节点P 是其父亲G 的左子节点。如果它是右子节点,情形4和情形5中的左和右应当对调。
情形4: 父节点P是红色而叔父节点U是黑色或缺少; 还有,新节点N是其父节点P的右子节点,而父节点P又是其父节点的左子节点。在这种情形下,我们进行一次左旋转调换新节点和其父节点的角色; 接着,我们按情形5处理以前的父节点P。这导致某些路径通过它们以前不通过的新节点N或父节点P中的一个,但是这两个节点都是红色的,所以性质3没有失效。
情况 4 示意图

情形5:父节点P是红色而叔父节点U 是黑色或缺少,新节点N 是其父节点的左子节点,而父节点P又是其父节点G的左子节点。在这种情形下,我们进行针对祖父节点G 的一次右旋转; 在旋转产生的树中,以前的父节点P现在是新节点N和以前的祖父节点G 的父节点。我们知道以前的祖父节点G是黑色,否则父节点P就不可能是红色 (如果 P 和 G 都是红色就违反了性质2,所以 G 必须是黑色)。我们切换以前的父节点P和祖父节点G的颜色,结果的树满足性质2。性质3也仍然保持满足,因为通过这三个节点中任何一个的所有路径以前都通过祖父节点G ,现在它们都通过以前的父节点P。在各自的情形下,这都是三个节点中唯一的黑色节点。
情况 5 示意图

2、删除

    如果需要删除的节点有两个儿子,那么问题可以被转化成删除另一个只有一个儿子的节点的问题(为了表述方便,这里所指的儿子,为非叶子节点的儿子)。对于二叉查找树,在删除带有两个非叶子儿子的节点的时候,我们找到要么在它的左子树中的最大元素、要么在它的右子树中的最小元素,并把它的值转移到要删除的节点中(如在这里所展示的那样)。我们接着删除我们从中复制出值的那个节点,它必定有少于两个非叶子的儿子。因为只是复制了一个值而不违反任何属性,这就把问题简化为如何删除最多有一个儿子的节点的问题。它不关心这个节点是最初要删除的节点还是我们从中复制出值的那个节点。
    在本文余下的部分中,我们只需要讨论删除只有一个儿子的节点(如果它两个儿子都为空,即均为叶子,我们任意将其中一个看作它的儿子)。如果我们删除一个红色节点,它的父亲和儿子一定是黑色的。所以我们可以简单的用它的黑色儿子替换它,并不会破坏属性2和3。通过被删除节点的所有路径只是少了一个红色节点,这样可以继续保证属性3。另一种简单情况是在被删除节点是黑色而它的儿子是红色的时候。如果只是去除这个黑色节点,用它的红色儿子顶替上来的话,会破坏属性2,但是如果我们重绘它的儿子为黑色,则曾经通过它的所有路径将通过它的黑色儿子,这样可以继续保持属性2。
    需要进一步讨论的是在要删除的节点和它的儿子二者都是黑色的时候,这是一种复杂的情况。我们首先把要删除的节点替换为它的儿子。出于方便,称呼这个儿子为N,称呼它的兄弟(它父亲的另一个儿子)为S。在下面的示意图中,我们还是使用P称呼N的父亲,SL称呼S的左儿子,SR称呼S的右儿子。

    如果 N 和它初始的父亲是黑色,则删除它的父亲导致通过 N 的路径都比不通过它的路径少了一个黑色节点。因为这违反了属性 3,树需要被重新平衡。有几种情况需要考虑:
情况 1: N 是新的根。在这种情况下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以属性都保持着。
注意: 在情况2、5和6下,我们假定 N 是它父亲的左儿子。如果它是右儿子,则在这些情况下的左和右应当对调。
情况 2: S 是红色。在这种情况下我们在N的父亲上做左旋转,把红色兄弟转换成N的祖父。我们接着对调 N 的父亲和祖父的颜色。尽管所有的路径仍然有相同数目的黑色节点,现在 N 有了一个黑色的兄弟和一个红色的父亲,所以我们可以接下去按 4、5或6情况来处理。(它的新兄弟是黑色因为它是红色S的一个儿子。)
情况 2 示意图
情况 3: N 的父亲、S 和 S 的儿子都是黑色的。在这种情况下,我们简单的重绘 S 为红色。结果是通过S的所有路径, 它们就是以前不通过 N 的那些路径,都少了一个黑色节点。因为删除 N 的初始的父亲使通过 N 的所有路径少了一个黑色节点,这使事情都平衡了起来。但是,通过 P 的所有路径现在比不通过 P 的路径少了一个黑色节点,所以仍然违反属性3。要修正这个问题,我们要从情况 1 开始,在 P 上做重新平衡处理。
情况 3 示意图
情况 4: S 和 S 的儿子都是黑色,但是 N 的父亲是红色。在这种情况下,我们简单的交换 N 的兄弟和父亲的颜色。这不影响不通过 N 的路径的黑色节点的数目,但是它在通过 N 的路径上对黑色节点数目增加了一,添补了在这些路径上删除的黑色节点。
情况 4 示意图


情况 5: S 是黑色,S 的左儿子是红色,S 的右儿子是黑色,而 N 是它父亲的左儿子。在这种情况下我们在 S 上做右旋转,这样 S 的左儿子成为 S 的父亲和 N 的新兄弟。我们接着交换 S 和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在 N 有了一个右儿子是红色的黑色兄弟,所以我们进入了情况 6。N 和它的父亲都不受这个变换的影响。
情况 5 示意图
情况 6: S 是黑色,S 的右儿子是红色,而 N 是它父亲的左儿子。在这种情况下我们在 N 的父亲上做左旋转,这样 S 成为 N 的父亲和 S 的右儿子的父亲。我们接着交换 N 的父亲和 S 的颜色,并使 S 的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以属性 1 没有被违反。但是,N 现在增加了一个黑色祖先: 要么 N 的父亲变成黑色,要么它是黑色而 S 被增加为一个黑色祖父。所以,通过 N 的路径都增加了一个黑色节点。此时,如果一个路径不通过 N,则有两种可能性:
  • 它通过 N 的新兄弟。那么它以前和现在都必定通过 S 和 N 的父亲,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。
  • 它通过 N 的新叔父,S 的右儿子。那么它以前通过 S、S 的父亲和 S 的右儿子,但是现在只通过 S,它被假定为它以前的父亲的颜色,和 S 的右儿子,它被从红色改变为黑色。合成效果是这个路径通过了同样数目的黑色节点。
在任何情况下,在这些路径上的黑色节点数目都没有改变。所以我们恢复了属性 2。在示意图中的白色节点可以是红色或黑色,但是在变换前后都必须指定相同的颜色。
情况 6 示意图

四、源码实现

struct Key {
    int value;
};
struct RBTNode {
    Key key;
    int lcount;
    int rcount;
    RBTNode* lchild;
    RBTNode* rchild;
    RBTNode* parent;
    bool color;
};
class RBT {
private:
    const static bool RED = true;
    const static bool BLACK = false;
    RBTNode* m_null;
    RBTNode* m_root;
    void clear() {//
        RBTNode* p = m_root;
        while (p != m_null) {
            if (p->lchild != m_null) {
                p = p->lchild;
            }else if (p->rchild != m_null) {
                p = p->rchild;
            }else {
                RBTNode* temp = p;
                p = p->parent;
                if (temp == p->lchild) {
                    p->lchild = m_null;
                }else {
                    p->rchild = m_null;
                }
                delete temp;
            }
        }
        m_root = m_null;
    }
    void delFixup(RBTNode* delNode) {
        RBTNode* p = delNode;
        while (p != m_root && p->color == BLACK) {
            if (p == p->parent->lchild) {
                RBTNode* sibling = p->parent->rchild;
                if (sibling->color == RED) {
                    sibling->color = BLACK;
                    p->parent->color = RED;
                    leftRotate(p->parent);
                    sibling = p->parent->rchild;
                }
                if (sibling->lchild->color == BLACK
                    && sibling->rchild->color == BLACK
                    ) {
                        sibling->color = RED;
                        p = p->parent;
                }else {
                    if (sibling->rchild->color == BLACK) {
                        sibling->lchild->color = BLACK;
                        sibling->color = RED;
                        rightRotate(sibling);
                        sibling = sibling->parent;
                    }
                    sibling->color = sibling->parent->color;
                    sibling->parent->color = BLACK;
                    sibling->rchild->color = BLACK;
                    leftRotate(sibling->parent);
                    p = m_root;
                }
            }else {
                RBTNode* sibling = p->parent->lchild;
                if (sibling->color == RED) {
                    sibling->color = BLACK;
                    p->parent->color = RED;
                    rightRotate(p->parent);
                    sibling = p->parent->lchild;
                }
                if (sibling->lchild->color == BLACK
                    && sibling->rchild->color == BLACK
                    ) {
                        sibling->color = RED;
                        p = p->parent;
                }else {
                    if (sibling->lchild->color == BLACK) {
                        sibling->rchild->color = BLACK;
                        sibling->color = RED;
                        leftRotate(sibling);
                        sibling = sibling->parent;
                    }
                    sibling->color = sibling->parent->color;
                    sibling->parent->color = BLACK;
                    sibling->lchild->color = BLACK;



                    rightRotate(sibling->parent);
                    p = m_root;
                }
            }
        }
        p->color = BLACK;
    }
    void insertFixup(RBTNode* insertNode) {
        RBTNode* p = insertNode;
        while (p->parent->color == RED) {
            if (p->parent == p->parent->parent->lchild) {
                RBTNode* parentRight = p->parent->parent->rchild;
                if (parentRight->color == RED) {
                    p->parent->color = BLACK;
                    parentRight->color = BLACK;
                    p->parent->parent->color = RED;
                    p = p->parent->parent;
                }else {
                    if (p == p->parent->rchild) {
                        p = p->parent;
                        leftRotate(p);
                    }
                    p->parent->color = BLACK;
                    p->parent->parent->color = RED;
                    rightRotate(p->parent->parent);
                }
            }else {
                RBTNode* parentLeft = p->parent->parent->lchild;
                if (parentLeft->color == RED) {
                    p->parent->color = BLACK;
                    parentLeft->color = BLACK;
                    p->parent->parent->color = RED;
                    p = p->parent->parent;
                }else {
                    if (p == p->parent->lchild) {
                        p = p->parent;
                        rightRotate(p);
                    }
                    p->parent->color = BLACK;
                    p->parent->parent->color = RED;
                    leftRotate(p->parent->parent);
                }
            }
        }
        m_root->color = BLACK;
    }

    inline int keyCmp(const Key& key1, const Key& key2) {
        //比较两个Key的大小。这里可能有更复杂的比较,如字符串比较等。
        return key1.value - key2.value;
    }

    inline void leftRotate(RBTNode* node) {
        //把一个节点向左下方移一格,并让他原来的右子节点代替它的位置。
        RBTNode* right = node->rchild;
        node->rchild = right->lchild;
        node->rcount = right->lcount;
        node->rchild->parent = node;
        right->parent = node->parent;
        if (right->parent == m_null) {
            m_root = right;
        }else if (node == node->parent->lchild) {
            node->parent->lchild = right;
        }else {
            node->parent->rchild = right;
        }
        right->lchild = node;
        right->lcount += node->lcount + 1;
        node->parent = right;
    }

    inline void rightRotate(RBTNode* node) {
        //把一个节点向右下方移一格,并让他原来的左子节点代替它的位置。
        RBTNode* left = node->lchild;
        node->lchild = left->rchild;
        node->lcount = left->rcount;
        node->lchild->parent = node;
        left->parent = node->parent;
        if (left->parent == m_null) {
            m_root = left;
        }else if (node == node->parent->lchild) {
            node->parent->lchild = left;
        }else {
            node->parent->rchild = left;
        }
        left->rchild = node;
        left->rcount += node->rcount + 1;
        node->parent = left;
    }

    RBTNode* treeMax(RBTNode* root) {//找到子树中最大的一个节点
        RBTNode* result = root;
        while (result->rchild != m_null) {
            result = result->rchild;
        }
        return result;
    }

    RBTNode* treeMin(RBTNode* root) {//找到子树中最小的一个节点
        RBTNode* result = root;
        while (result->lchild != m_null) {
            result = result->lchild;
        }
        return result;
    }
public:
    RBT() {
        m_null = new RBTNode;
        m_null->color = BLACK;
        m_null->lchild = m_null->rchild = m_null;
        m_root = m_null;
    }
    ~RBT() {
        clear();
        delete m_null;
    }

    RBTNode* atIndex(int i) {//找到从小到大排序后下标为i的节点。i从0开始。
        RBTNode* result = m_root;
        if (i > result->lcount + result->rcount) {
            result = NULL;
        }else {
            while (i != result->lcount) {
                if (i < result->lcount) {
                    result = result->lchild;
                }else {
                    i -= result->lcount + 1;
                    result = result->rchild;
                }
            }
        }
        return result;
    }

    void del(RBTNode* node) {//删除一个节点
        RBTNode* toDel = node;
        if (node->lchild != m_null && node->rchild != m_null) {
            toDel = treeNext(node);//找到中序后继:即右子树最左节点
        }
        RBTNode* temp = toDel;
        while (temp->parent != m_null) {
            if (temp == temp->parent->lchild) {
                temp->parent->lcount--;
            }else {
                temp->parent->rcount--;
            }
            temp = temp->parent;
        }
        RBTNode* replace = toDel->lchild != m_null? toDel->lchild: toDel->rchild;
        replace->parent = toDel->parent;
        if (replace->parent == m_null) {
            m_root = replace;
        }else if (toDel == toDel->parent->lchild) {
            replace->parent->lchild = replace;
        }else {
            replace->parent->rchild = replace;
        }
        if (toDel != node) {
            node->key = toDel->key;
        }
        if (toDel->color == BLACK) {
            //修改树,以保持平衡。
            delFixup(replace);
        }
        delete toDel;
    }

    void insert(const Key& key) {//插入一个节点
        RBTNode* node = new RBTNode;

        node->key = key;
        node->lcount = 0;
        node->rcount = 0;
        node->lchild = m_null;
        node->rchild = m_null;
        node->color = RED;

        RBTNode* p = m_root;
        RBTNode* leaf = m_null;
        while (p != m_null) {
            leaf = p;
            if (keyCmp(node->key, p->key) < 0) {
                p->lcount++;
                p = p->lchild;
            }else {
                p->rcount++;
                p = p->rchild;
            }
        }
        node->parent = leaf;
        if (leaf == m_null) {//如果是空树。
            m_root = node;
        }else if (keyCmp(node->key, leaf->key) < 0) {
            leaf->lchild = node;
        }else {
            leaf->rchild = node;
        }
        //修改树,以保持平衡。
        insertFixup(node);
    }
    int nodeCount() {
        return m_root != m_null? m_root->lcount + m_root->rcount + 1: 0;
    }

    RBTNode* search(const Key& key) {//按照key查找一个节点。
        RBTNode* result = m_root;
        while (result != m_null && keyCmp(key, result->key) != 0) {
            result = keyCmp(key, result->key) < 0 ? result->lchild : result->rchild;
        }
        return result == m_null? NULL: result;
    }

    void toArray(int* array) {//把树中节点的值放进一个数组。
        RBTNode* p = treeMin(m_root);
        int i = 0;
        while (p != m_null) {
            array[i] = p->key.value;
            i++;
            p = treeNext(p);
        }
    }

    RBTNode* treeNext(RBTNode* node) {//一个节点在中序遍列中的下一个节点。后继
        RBTNode* result;
        if (node->rchild != m_null) {
            result = treeMin(node->rchild);
        }else {
            result = node->parent;
            RBTNode* temp = node;
            while (result != m_null && temp == result->rchild) {
                temp = result;
                result = result->parent;
            }
        }
        return result;
    }

    RBTNode* treePre(RBTNode* node) {//一个节点在中序遍列中的前一个节点。前驱
        RBTNode* result;
        if (node->lchild != m_null) {
            result = treeMax(node->lchild);
        }else {
            result = node->parent;
            RBTNode* temp = node;
            while (result != m_null && temp == result->lchild) {
                temp = result;
                result = result->parent;
            }
        }
        return result;
    }
};

五、参考文献

3、数据结构基础(C++语言版)P398

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值