redblacktree_rebalance_red_violation.hpp

Back to Red-black tree

pastel/sys/redblacktree/

#ifndef PASTELSYS_REDBLACKTREE_REBALANCE_RED_VIOLATION_HPP
#define PASTELSYS_REDBLACKTREE_REBALANCE_RED_VIOLATION_HPP

#include "pastel/sys/redblacktree.h"

namespace Pastel
{

    template <typename Settings, template <typename> class Customization>
    auto RedBlackTree<Settings, Customization>::rebalanceRedViolation(
        Node* node)
    -> Node*
    {
        Node* parent = 0;
        while (true)
        {
            // The loop invariant is as follows:

            // At the start of the loop 
            // * 'node' is red (and therefore not the sentinel),
            // * the subtree rooted at 'node' is a red-black tree,
            // * the propagation data is up-to-date in
            // the subtree rooted at 'node'.

            ASSERT(!node->isSentinel());
            ASSERT(node->red());

            parent = node->parent();
            if (parent->black())
            {
                //     |B     .
                //     p      .
                //   R/ \     .
                //   n   3    .
                // B/ \B      .
                // 1   2      .

                // We have fixed all the violations. 
                break;
            }

            // From now on the parent is red. 
            ASSERT(parent->red());

            // It follows that the parent exists.
            ASSERT(!parent->isSentinel());

            Node* grandParent = parent->parent();
            if (grandParent->isSentinel())
            {
                //     R             B      .
                //     p            p      .
                //   R/ \B ==>     R/ \B    .
                //   n   3        n   3    .
                // B/ \B        B/ \B      .
                // 1   2        1   2      .

                parent->setBlack();
                ++blackHeight_;

                break;
            }

            // From now on the grand-parent exists.
            ASSERT(!grandParent->isSentinel());

            bool parentIsRight = (parent == grandParent->right());
            Node* uncle = grandParent->child(!parentIsRight);

            if (uncle->black())
            {
                // If the parent is red, and the uncle
                // is black, then the violation is that
                // 'parent' and 'node' are two consecutive
                // red nodes.

                bool nodeIsRight = (node == parent->right());
                if (nodeIsRight != parentIsRight)
                {
                    //        |B               |B         .
                    //        g                g          .
                    //      R/ \B            R/ \B        .
                    //     p     u    ==>   n     u       .
                    //   B/ \R  / \       R/ \B  / \      .
                    //   1   n 4   5      p   3 4   5     .
                    //     B/ \B        B/ \B             .
                    //     2   3        1   2             .

                    // If the grandparent-parent-node forms
                    // a turn, then we reduce it to a chain.
                    // This does not yet get rid of the red-red
                    // violation, but reduces it so that
                    // the next case can handle it.

                    rotate(parent, !nodeIsRight);

                    update(parent);
                    update(node);

                    // Continue as if we were in the
                    // parent node.
                    std::swap(node, parent);
                }

                //        |B               |B          .
                //        g                p           .
                //      R/ \B            R/ \R         .
                //     p     u    ==>   n     g        .
                //   R/ \B  / \       B/ \B B/ \B      .
                //   n   3 4   5      1   2 3   u      .
                // B/ \B                       / \     .
                // 1   2                      4   5    .

                // The 'node' is the left child of the
                // 'parent'. In this case we do as above.

                rotate(grandParent, !parentIsRight);
                parent->setBlack();
                grandParent->setRed();

                update(grandParent);

                // We have fixed all the violations.
                break;
            }

            // From now on, both the parent and the uncle
            // must be red.

            //        |B               |R        .
            //        g                g         .
            //      R/ \R            B/ \B       .
            //     p     u    ==>   p     u      .
            //   R/ \B B/ \B      R/ \B B/ \B    .
            //   n   3 4   5      n   3 4   5    .
            // B/ \B            B/ \B            .
            // 1   2            1   2            .

            // If the parent and the uncle are red,
            // then the only violation is that 'node'
            // is a red child of the red 'parent' node.
            // This can be fixed by changing parent
            // and uncle black, and grandparent red.

            parent->setBlack();
            uncle->setBlack();
            grandParent->setRed();

            update(parent);
            update(uncle);
            update(grandParent);

            // This is the only case which recurses upwards.
            // The loop invariant now holds for the grand-parent,
            // so we will continue from there.
            node = grandParent;
        }

        // We will maintain the invariant that the
        // root node is black, although this is not
        // part of our definition of a red-black tree.
        setRootBlack();

        // Note that we deliberately do not update propagation
        // data from 'parent' upwards. This is because sometimes
        // we wish to amortize the cost of multiple updates,
        // as is done in split(). Instead, we return the node
        // from which the propagation should be updated upwards.

        return parent;
    }

}

#endif