Source Code Analysis and Interpretation of Red and Black Trees

Keywords: Java less

Introduction of Red and Black Trees

Red-Black Tree (R-B Tree) is a special binary search tree.
The red-black tree is a special binary search tree, which means that it satisfies the characteristics of the binary search tree: the key value contained in any node is greater than or equal to the key value of the left child, less than or equal to the key value of the right child.
In addition to this feature, the red-black tree also contains a lot of additional information.

Each node of a red-black tree has a storage bit representing the color of the node, which is red or black.
The characteristics of red-black trees:
(1) Each node is either black or red.
(2) The root node is black.
(3) Each leaf node is black. [Note: Here the leaf node refers to the empty leaf node! ]
(4) If a node is red, its children must be black.
(5) All paths from a node to its descendants contain the same number of black nodes.

With regard to its characteristics, it should be noted that:
First, the leaf nodes in feature (3) are only empty (NIL or null) nodes.
Second, feature (5) ensures that no path is twice as long as any other path. Therefore, the red-black tree is a binary tree which is relatively close to equilibrium.

The maps of mangrove trees are as follows:

Java implementation of red-black tree (code description)

The basic operation of red-black tree is to add, delete and rotate. After adding or deleting the red-black tree, the rotation method will be used. Why? The reason is very simple. After adding or deleting the nodes in the red-black tree, the red-black tree changes. It may not satisfy the five properties of the red-black tree, and it is no longer a red-black tree, but an ordinary tree. By rotating, the tree can be made red and black again. Simply put, the purpose of rotation is to maintain the characteristics of red and black trees.
Rotation consists of two kinds: left-handed and right-handed. The basic operation of red-black tree is introduced below.

1. Basic Definitions

public class RBTree<T extends Comparable<T>> {

    private RBTNode<T> mRoot;    // Root node

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    public class RBTNode<T extends Comparable<T>> {
        boolean color;        // colour
        T key;                // Keyword (key value)
        RBTNode<T> left;    // left child
        RBTNode<T> right;    // Right child
        RBTNode<T> parent;    // Parent node

        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

    }
    ...
}

RBTree is the corresponding class of red-black tree, and RBTNode is the node class of red-black tree. In RBTree, the root node mRoot and the related API of the red-black tree are included.
Note: I overloaded many functions in the implementation of the Red and Black Tree API. The reason for overloading is that APIs are internal interfaces and some are external interfaces. The second reason is to make the structure clearer.

 

2. Left-handed

Left-handed to x means "turning x into a left node".

Left-handed implementation code

/* 
 * Rotate the node (x) of the red-black tree left
 *
 * Left-handed schematic diagram (left-handed for node x):
 *      px                              px
 *     /                               /
 *    x                               y                
 *   /  \      --(Left-handed) -../\#
 *  lx   y                          x  ry     
 *     /   \                       /  \
 *    ly   ry                     lx  ly  
 *
 *
 */
private void leftRotate(RBTNode<T> x) {
    // Set the right child of x to y
    RBTNode<T> y = x.right;

    // Set "the left child of y" as "the right child of x";
    // If the left child of Y is not empty, set "x" as "the father of the left child of y"
    x.right = y.left;
    if (y.left != null)
        y.left.parent = x;

    // Set "father of x" as "father of y"
    y.parent = x.parent;

    if (x.parent == null) {
        this.mRoot = y;            // If the "father of x" is an empty node, set y to the root node
    } else {
        if (x.parent.left == x)
            x.parent.left = y;    // If x is the left child of its parent node, set y to "the left child of the parent node of x"
        else
            x.parent.right = y;    // If x is the left child of its parent node, set y to "the left child of the parent node of x"
    }
    
    // Set "x" to "the left child of y"
    y.left = x;
    // Set "x's parent node" to "y"
    x.parent = y;
}

3. Right-handed

Left-handed y means "turn y into a right node".

Right-handed implementation code

/* 
 * Rotate the node (y) of the red-black tree to the right
 *
 * Right-hand sketch (left-hand for node y):
 *            py                               py
 *           /                                /
 *          y                                x                  
 *         /  \      --(Right-handed) -../\#
 *        x   ry                           lx   y  
 *       / \                                   / \                   #
 *      lx  rx                                rx  ry
 * 
 */
private void rightRotate(RBTNode<T> y) {
    // Setting x is the left child of the current node.
    RBTNode<T> x = y.left;

    // Set "the right child of x" as "the left child of y";
    // If "the right child of x" is not empty, set "y" as "the father of the right child of x"
    y.left = x.right;
    if (x.right != null)
        x.right.parent = y;

    // Set "y's father" as "x's father"
    x.parent = y.parent;

    if (y.parent == null) {
        this.mRoot = x;            // If the "father of y" is an empty node, set x to the root node
    } else {
        if (y == y.parent.right)
            y.parent.right = x;    // If y is the right child of its parent node, set x to "the right child of y's parent node"
        else
            y.parent.left = x;    // (y is the left child of its parent node) Set x to "the left child of the parent node of x"
    }

    // Set "y" to "the right child of x"
    x.right = y;

    // Set "y's parent node" to "x"
    y.parent = x;
}

4. Add

What steps do you need to take to insert a node into a red-black tree? Firstly, the red-black tree is treated as a binary search tree, and the nodes are inserted; then, the nodes are colored red; finally, the tree is modified by a series of operations such as "rotation and re-coloring" to make it become a red-black tree again. Detailed description is as follows:
The first step is to treat the red-black tree as a binary search tree and insert the nodes.
The red-black tree itself is a binary search tree, after inserting nodes, the tree is still a binary search tree. This means that the tree's key values are still in order. In addition, whether left-handed or right-handed, if the tree is a binary search tree before rotation, it must be a binary search tree after rotation. This means that any rotation and re-shading operation will not change the fact that it is still a binary search tree.
OK Next, we'll try our best to rotate and re-color the tree so that it can become a red-black tree again! ____________

Step 2: Coloring the inserted node red.
Why is the colour red, not black? Why? Before answering, we need to revisit the characteristics of red-black trees:
(1) Each node is either black or red.
(2) The root node is black.
(3) Each leaf node is black. [Note: Here the leaf node refers to the empty leaf node! ]
(4) If a node is red, its children must be black.
(5) All paths from a node to its descendants contain the same number of black nodes.
Coloring the inserted node red will not violate the "feature (5)"! Less violation of a feature means less of what we need to deal with. Next, we should strive to make the tree satisfy other properties; if satisfied, it will be a red-black tree. o() o() Haha

Step 3: Make it a red-black tree again by a series of operations such as rotation or shading.
In the second step, after the insertion node is colored "red", it will not violate "feature (5)". So what exactly does it violate?
For feature (1), it is clear that it will not be violated. Because we have painted it red.
For feature (2), it is clear that it will not be violated. In the first step, we treat the red-black tree as a binary search tree, and then perform the insertion operation. According to the characteristics of binary lookup number, insertion operation will not change the root node. So the root node is still black.
For feature (3), it is clear that it will not be violated. Leaf nodes here refer to empty leaf nodes, inserting non-empty nodes will not affect them.
For "feature (4)", it is possible to violate it!
Next, find a way to make it "meet the characteristics (4)" so that the tree can be reconstructed into a red-black tree.

Implementation Code for Adding Operations

/* 
 * Inserting Nodes into Red-Black Trees
 *
 * Description of parameters:
 *     node Inserted Nodes//corresponding Nodes in Introduction to Algorithms
 */
private void insert(RBTNode<T> node) {
    int cmp;
    RBTNode<T> y = null;
    RBTNode<T> x = this.mRoot;

    // 1. The red-black tree is treated as a binary search tree, and the nodes are added to the binary search tree.
    while (x != null) {
        y = x;
        cmp = node.key.compareTo(x.key);
        if (cmp < 0)
            x = x.left;
        else
            x = x.right;
    }

    node.parent = y;
    if (y!=null) {
        cmp = node.key.compareTo(y.key);
        if (cmp < 0)
            y.left = node;
        else
            y.right = node;
    } else {
        this.mRoot = node;
    }

    // 2. Set the node's color to red
    node.color = RED;

    // 3. Revise it to a binary search tree
    insertFixUp(node);
}

/* 
 * Create a new node (key) and insert it into the red-black tree
 *
 * Description of parameters:
 *     key Key values for inserting nodes
 */
public void insert(T key) {
    RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);

    // If the new node fails, it returns.
    if (node != null)
        insert(node);
}

The function of the internal interface, insert(node), is to insert "node" nodes into the red-black tree.
The function of the external interface, insert(key), is to add "key" to the red-black tree.

Implementation Code for Adding Modification Operations

/*
 * Red-Black Tree Insertion Correction Function
 *
 * After inserting a node into the red-black tree (out of balance), the function is called again.
 * The aim is to reshape it into a red-black tree.
 *
 * Description of parameters:
 *     node Inserted Nodes//Corresponding to z in Introduction to Algorithms
 */
private void insertFixUp(RBTNode<T> node) {
    RBTNode<T> parent, gparent;

    // If "the parent node exists and the color of the parent node is red"
    while (((parent = parentOf(node))!=null) && isRed(parent)) {
        gparent = parentOf(parent);

        //If the "parent node" is the "left child of the grandfather node"
        if (parent == gparent.left) {
            // Case 1 condition: Uncle node is red
            RBTNode<T> uncle = gparent.right;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2 condition: Uncle is black, and the current node is the right child
            if (parent.right == node) {
                RBTNode<T> tmp;
                leftRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3 condition: Uncle is black, and the current node is left child.
            setBlack(parent);
            setRed(gparent);
            rightRotate(gparent);
        } else {    //If "the parent node of z" is "the right child of the grandfather node of z"
            // Case 1 condition: Uncle node is red
            RBTNode<T> uncle = gparent.left;
            if ((uncle!=null) && isRed(uncle)) {
                setBlack(uncle);
                setBlack(parent);
                setRed(gparent);
                node = gparent;
                continue;
            }

            // Case 2 condition: Uncle is black, and the current node is left child
            if (parent.left == node) {
                RBTNode<T> tmp;
                rightRotate(parent);
                tmp = parent;
                parent = node;
                node = tmp;
            }

            // Case 3 condition: Uncle is black, and the current node is the right child.
            setBlack(parent);
            setRed(gparent);
            leftRotate(gparent);
        }
    }

    // Set the root node to black
    setBlack(this.mRoot);
}

The function of insert FixUp (node) is to correspond to "the third step mentioned above". It is an internal interface.

5. Delete operation

Delete a node in a red-black tree. The operations that need to be performed are as follows: firstly, the red-black tree is treated as a binary search tree, and the node is deleted from the binary search tree; secondly, the tree is modified by a series of "rotation and re-coloring" to make it a red-black tree again. Detailed description is as follows:
The first step is to treat the red-black tree as a binary search tree and delete the nodes.
This is the same as "deleting nodes from a conventional binary lookup tree". There are three cases:
The deleted node has no son, that is, leaf node. Then, delete the node directly and OK.
The deleted node has only one son. Then, delete the node directly and replace it with its unique child node.
The deleted node has two sons. Then, first find its successor node; then copy "the content of its successor node" to "the content of the node"; then delete "its successor node". Here, the successor node is equivalent to a stand-in. After copying the content of the successor node to the "deleted node", the successor node is deleted. This cleverly transforms the problem into the case of "deleting successor nodes". Next, consider successor nodes. In the case that the "deleted node" has two non-empty sub-nodes, its successor nodes can not be Gemini non-empty. Since the "successor node" can not be empty, it means that the "successor node of the node" has either no son or only one son. If there is no son, it will be dealt with according to "circumstance 1"; if there is only one son, it will be dealt with according to "circumstance 2".

The second step is to modify the tree through a series of "rotation and re-coloring" to make it a red-black tree again.
Because deleting nodes in Step One may violate the characteristics of red-black trees. So the tree needs to be modified by "rotation and re-coloring" to make it a red-black tree again.

Implementation code for deletion operation

/* 
 * Delete the node and return the deleted node
 *
 * Description of parameters:
 *     node Deleted nodes
 */
private void remove(RBTNode<T> node) {
    RBTNode<T> child, parent;
    boolean color;

    // The deleted node "left and right children are not empty" situation.
    if ( (node.left!=null) && (node.right!=null) ) {
        // The successor node of the deleted node. (called "replacement node")
        // Use it to replace the "deleted node" position, and then remove the "deleted node".
        RBTNode<T> replace = node;

        // Getting successor nodes
        replace = replace.right;
        while (replace.left != null)
            replace = replace.left;

        // "Node node" is not the root node (only the root node does not have a parent node)
        if (parentOf(node)!=null) {
            if (parentOf(node).left == node)
                parentOf(node).left = replace;
            else
                parentOf(node).right = replace;
        } else {
            // The "node node" is the root node, updating the root node.
            this.mRoot = replace;
        }

        // Child is the right child of "replacing node", and also the node that needs to be adjusted.
        // "Replacement Node" definitely does not have a left child! Because it is a successor node.
        child = replace.right;
        parent = parentOf(replace);
        // Save the color of the Replacement Node
        color = colorOf(replace);

        // "Deleted node" is "parent of its successor node"
        if (parent == node) {
            parent = replace;
        } else {
            // child is not empty
            if (child!=null)
                setParent(child, parent);
            parent.left = child;

            replace.right = node.right;
            setParent(node.right, replace);
        }

        replace.parent = node.parent;
        replace.color = node.color;
        replace.left = node.left;
        node.left.parent = replace;

        if (color == BLACK)
            removeFixUp(child, parent);

        node = null;
        return ;
    }

    if (node.left !=null) {
        child = node.left;
    } else {
        child = node.right;
    }

    parent = node.parent;
    // Save the color of the Replacement Node
    color = node.color;

    if (child!=null)
        child.parent = parent;

    // "Node node" is not the root node
    if (parent!=null) {
        if (parent.left == node)
            parent.left = child;
        else
            parent.right = child;
    } else {
        this.mRoot = child;
    }

    if (color == BLACK)
        removeFixUp(child, parent);
    node = null;
}

/* 
 * Delete the node (z) and return the deleted node
 *
 * Description of parameters:
 *     tree Root Node of Red-Black Tree
 *     z Deleted nodes
 */
public void remove(T key) {
    RBTNode<T> node; 

    if ((node = search(mRoot, key)) != null)
        remove(node);
}

Internal interface - remove(node) is used to insert "node" nodes into the red-black tree.
External interface -- remove(key) deletes the node whose key value is key in the red-black tree.

Implementing Code for Deleting Correction Operations

/*
 * Red-Black Tree Delete Correction Function
 *
 * After deleting the insertion node from the red-black tree (the red-black tree is out of balance), the function is called again.
 * The aim is to reshape it into a red-black tree.
 *
 * Description of parameters:
 *     node Nodes to be amended
 */
private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
    RBTNode<T> other;

    while ((node==null || isBlack(node)) && (node != this.mRoot)) {
        if (parent.left == node) {
            other = parent.right;
            if (isRed(other)) {
                // Case 1: x's brother w is red  
                setBlack(other);
                setRed(parent);
                leftRotate(parent);
                other = parent.right;
            }

            if ((other.left==null || isBlack(other.left)) &&
                (other.right==null || isBlack(other.right))) {
                // Case 2: x's brother W is black, and w's two children are also black.  
                setRed(other);
                node = parent;
                parent = parentOf(node);
            } else {

                if (other.right==null || isBlack(other.right)) {
                    // Case 3: x's brother W is black, and w's left child is red, and his right child is black.  
                    setBlack(other.left);
                    setRed(other);
                    rightRotate(other);
                    other = parent.right;
                }
                // Case 4: x's brother W is black; and w's right child is red, and his left child is any color.
                setColor(other, colorOf(parent));
                setBlack(parent);
                setBlack(other.right);
                leftRotate(parent);
                node = this.mRoot;
                break;
            }
        } else {

            other = parent.left;
            if (isRed(other)) {
                // Case 1: x's brother w is red  
                setBlack(other);
                setRed(parent);
                rightRotate(parent);
                other = parent.left;
            }

            if ((other.left==null || isBlack(other.left)) &&
                (other.right==null || isBlack(other.right))) {
                // Case 2: x's brother W is black, and w's two children are also black.  
                setRed(other);
                node = parent;
                parent = parentOf(node);
            } else {

                if (other.left==null || isBlack(other.left)) {
                    // Case 3: x's brother W is black, and w's left child is red, and his right child is black.  
                    setBlack(other.right);
                    setRed(other);
                    leftRotate(other);
                    other = parent.left;
                }

                // Case 4: x's brother W is black; and w's right child is red, and his left child is any color.
                setColor(other, colorOf(parent));
                setBlack(parent);
                setBlack(other.left);
                rightRotate(parent);
                node = this.mRoot;
                break;
            }
        }
    }

    if (node!=null)
        setBlack(node);
}

removeFixup(node, parent) corresponds to "the third step mentioned above". It is an internal interface.

 

Java implementation of red-black tree (full source code)

Below is the complete code of the implementation of the red-black tree and the corresponding test procedures.
(1) In addition to the basic operations mentioned above, such as "left-handed", "right-handed", "add", "delete", "traversal", "search", "print", "minimum", "maximum", "create" and "destroy" interfaces are also implemented.
(2) Functional interfaces are mostly divided into internal interfaces and external interfaces. The internal interface is the private function, while the external interface is the public function.
(3) Insert and delete detection switches are provided in the test code. The default is off, and the open method can refer to "Description in Code". It is suggested to draw the red and black trees on the draft after opening the switch.

Implementation file of red-black tree (RBTree.java)

/**
 * Java Language: Red and Black Trees
 *
 * @author xxx
 * @date 2019/8/13
 */

public class RBTree<T extends Comparable<T>> {

    private RBTNode<T> mRoot;    // Root node

    private static final boolean RED   = false;
    private static final boolean BLACK = true;

    public class RBTNode<T extends Comparable<T>> {
        boolean color;        // colour
        T key;                // Keyword (key value)
        RBTNode<T> left;    // left child
        RBTNode<T> right;    // Right child
        RBTNode<T> parent;    // Parent node

        public RBTNode(T key, boolean color, RBTNode<T> parent, RBTNode<T> left, RBTNode<T> right) {
            this.key = key;
            this.color = color;
            this.parent = parent;
            this.left = left;
            this.right = right;
        }

        public T getKey() {
            return key;
        }

        public String toString() {
            return ""+key+(this.color==RED?"(R)":"B");
        }
    }

    public RBTree() {
        mRoot=null;
    }

    private RBTNode<T> parentOf(RBTNode<T> node) {
        return node!=null ? node.parent : null;
    }
    private boolean colorOf(RBTNode<T> node) {
        return node!=null ? node.color : BLACK;
    }
    private boolean isRed(RBTNode<T> node) {
        return ((node!=null)&&(node.color==RED)) ? true : false;
    }
    private boolean isBlack(RBTNode<T> node) {
        return !isRed(node);
    }
    private void setBlack(RBTNode<T> node) {
        if (node!=null)
            node.color = BLACK;
    }
    private void setRed(RBTNode<T> node) {
        if (node!=null)
            node.color = RED;
    }
    private void setParent(RBTNode<T> node, RBTNode<T> parent) {
        if (node!=null)
            node.parent = parent;
    }
    private void setColor(RBTNode<T> node, boolean color) {
        if (node!=null)
            node.color = color;
    }

    /*
     * Preorder traversal of "red and black trees"
     */
    private void preOrder(RBTNode<T> tree) {
        if(tree != null) {
            System.out.print(tree.key+" ");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }

    public void preOrder() {
        preOrder(mRoot);
    }

    /*
     * Mid-order traversal of "red and black trees"
     */
    private void inOrder(RBTNode<T> tree) {
        if(tree != null) {
            inOrder(tree.left);
            System.out.print(tree.key+" ");
            inOrder(tree.right);
        }
    }

    public void inOrder() {
        inOrder(mRoot);
    }


    /*
     * Post-order traversal of "red and black trees"
     */
    private void postOrder(RBTNode<T> tree) {
        if(tree != null)
        {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.key+" ");
        }
    }

    public void postOrder() {
        postOrder(mRoot);
    }


    /*
     * (Recursive implementation) Find the node whose key value is key in "red-black tree x"
     */
    private RBTNode<T> search(RBTNode<T> x, T key) {
        if (x==null)
            return x;

        int cmp = key.compareTo(x.key);
        if (cmp < 0)
            return search(x.left, key);
        else if (cmp > 0)
            return search(x.right, key);
        else
            return x;
    }

    public RBTNode<T> search(T key) {
        return search(mRoot, key);
    }

    /*
     * (Non-recursive implementation) Find the node whose key value is key in "red-black tree x"
     */
    private RBTNode<T> iterativeSearch(RBTNode<T> x, T key) {
        while (x!=null) {
            int cmp = key.compareTo(x.key);

            if (cmp < 0) 
                x = x.left;
            else if (cmp > 0) 
                x = x.right;
            else
                return x;
        }

        return x;
    }

    public RBTNode<T> iterativeSearch(T key) {
        return iterativeSearch(mRoot, key);
    }

    /* 
     * Find the smallest node: Return the smallest node of the red-black tree whose tree is the root node.
     */
    private RBTNode<T> minimum(RBTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.left != null)
            tree = tree.left;
        return tree;
    }

    public T minimum() {
        RBTNode<T> p = minimum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }
     
    /* 
     * Find the maximum node: Return the maximum node of the red-black tree whose tree is the root node.
     */
    private RBTNode<T> maximum(RBTNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.right != null)
            tree = tree.right;
        return tree;
    }

    public T maximum() {
        RBTNode<T> p = maximum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }

    /* 
     * Find the successor node of node (x). That is, to find the "smallest node" whose data value in the red-black tree is greater than that of the node.
     */
    public RBTNode<T> successor(RBTNode<T> x) {
        // If X has a right child, then "the successor node of x" is "the smallest node of the subtree rooted by its right child".
        if (x.right != null)
            return minimum(x.right);

        // If x has no right child. Then x has the following two possibilities:
        // (01) x is "a left child", and "the successor node of x" is "its father node".
        // (02) x is a "right child", then look for "the lowest father node of x, and the father node must have a left child", and the "lowest father node" found is "the successor node of x".
        RBTNode<T> y = x.parent;
        while ((y!=null) && (x==y.right)) {
            x = y;
            y = y.parent;
        }

        return y;
    }
     
    /* 
     * Find the precursor node of node (x). That is, to find the "maximum node" of "data value in red-black tree is less than that of the node".
     */
    public RBTNode<T> predecessor(RBTNode<T> x) {
        // If X has a left child, then "the precursor node of x" is "the largest node of the subtree rooted by its left child".
        if (x.left != null)
            return maximum(x.left);

        // If x has no left child. Then x has the following two possibilities:
        // (01) x is "a right child", and "the precursor node of x" is "its father node".
        // (01) x is "a left child", then look for "the lowest father node of x, and the father node must have the right child", and the "lowest father node" found is "the precursor node of x".
        RBTNode<T> y = x.parent;
        while ((y!=null) && (x==y.left)) {
            x = y;
            y = y.parent;
        }

        return y;
    }

    /* 
     * Rotate the node (x) of the red-black tree left
     *
     * Left-handed schematic diagram (left-handed for node x):
     *      px                              px
     *     /                               /
     *    x                               y                
     *   /  \      --(Left-handed) -../\#
     *  lx   y                          x  ry     
     *     /   \                       /  \
     *    ly   ry                     lx  ly  
     *
     *
     */
    private void leftRotate(RBTNode<T> x) {
        // Set the right child of x to y
        RBTNode<T> y = x.right;

        // Set "the left child of y" as "the right child of x";
        // If the left child of Y is not empty, set "x" as "the father of the left child of y"
        x.right = y.left;
        if (y.left != null)
            y.left.parent = x;

        // Set "father of x" as "father of y"
        y.parent = x.parent;

        if (x.parent == null) {
            this.mRoot = y;            // If the "father of x" is an empty node, set y to the root node
        } else {
            if (x.parent.left == x)
                x.parent.left = y;    // If x is the left child of its parent node, set y to "the left child of the parent node of x"
            else
                x.parent.right = y;    // If x is the left child of its parent node, set y to "the left child of the parent node of x"
        }
        
        // Set "x" to "the left child of y"
        y.left = x;
        // Set "x's parent node" to "y"
        x.parent = y;
    }

    /* 
     * Rotate the node (y) of the red-black tree to the right
     *
     * Right-hand sketch (left-hand for node y):
     *            py                               py
     *           /                                /
     *          y                                x                  
     *         /  \      --(Right-handed) -../\#
     *        x   ry                           lx   y  
     *       / \                                   / \                   #
     *      lx  rx                                rx  ry
     * 
     */
    private void rightRotate(RBTNode<T> y) {
        // Setting x is the left child of the current node.
        RBTNode<T> x = y.left;

        // Set "the right child of x" as "the left child of y";
        // If "the right child of x" is not empty, set "y" as "the father of the right child of x"
        y.left = x.right;
        if (x.right != null)
            x.right.parent = y;

        // Set "y's father" as "x's father"
        x.parent = y.parent;

        if (y.parent == null) {
            this.mRoot = x;            // If the "father of y" is an empty node, set x to the root node
        } else {
            if (y == y.parent.right)
                y.parent.right = x;    // If y is the right child of its parent node, set x to "the right child of y's parent node"
            else
                y.parent.left = x;    // (y is the left child of its parent node) Set x to "the left child of the parent node of x"
        }

        // Set "y" to "the right child of x"
        x.right = y;

        // Set "y's parent node" to "x"
        y.parent = x;
    }

    /*
     * Red-Black Tree Insertion Correction Function
     *
     * After inserting a node into the red-black tree (out of balance), the function is called again.
     * The aim is to reshape it into a red-black tree.
     *
     * Description of parameters:
     *     node Inserted Nodes//Corresponding to z in Introduction to Algorithms
     */
    private void insertFixUp(RBTNode<T> node) {
        RBTNode<T> parent, gparent;

        // If "the parent node exists and the color of the parent node is red"
        while (((parent = parentOf(node))!=null) && isRed(parent)) {
            gparent = parentOf(parent);

            //If the "parent node" is the "left child of the grandfather node"
            if (parent == gparent.left) {
                // Case 1 condition: Uncle node is red
                RBTNode<T> uncle = gparent.right;
                if ((uncle!=null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }

                // Case 2 condition: Uncle is black, and the current node is the right child
                if (parent.right == node) {
                    RBTNode<T> tmp;
                    leftRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }

                // Case 3 condition: Uncle is black, and the current node is left child.
                setBlack(parent);
                setRed(gparent);
                rightRotate(gparent);
            } else {    //If "the parent node of z" is "the right child of the grandfather node of z"
                // Case 1 condition: Uncle node is red
                RBTNode<T> uncle = gparent.left;
                if ((uncle!=null) && isRed(uncle)) {
                    setBlack(uncle);
                    setBlack(parent);
                    setRed(gparent);
                    node = gparent;
                    continue;
                }

                // Case 2 condition: Uncle is black, and the current node is left child
                if (parent.left == node) {
                    RBTNode<T> tmp;
                    rightRotate(parent);
                    tmp = parent;
                    parent = node;
                    node = tmp;
                }

                // Case 3 condition: Uncle is black, and the current node is the right child.
                setBlack(parent);
                setRed(gparent);
                leftRotate(gparent);
            }
        }

        // Set the root node to black
        setBlack(this.mRoot);
    }

    /* 
     * Inserting Nodes into Red-Black Trees
     *
     * Description of parameters:
     *     node Inserted Nodes//corresponding Nodes in Introduction to Algorithms
     */
    private void insert(RBTNode<T> node) {
        int cmp;
        RBTNode<T> y = null;
        RBTNode<T> x = this.mRoot;

        // 1. The red-black tree is treated as a binary search tree, and the nodes are added to the binary search tree.
        while (x != null) {
            y = x;
            cmp = node.key.compareTo(x.key);
            if (cmp < 0)
                x = x.left;
            else
                x = x.right;
        }

        node.parent = y;
        if (y!=null) {
            cmp = node.key.compareTo(y.key);
            if (cmp < 0)
                y.left = node;
            else
                y.right = node;
        } else {
            this.mRoot = node;
        }

        // 2. Set the node's color to red
        node.color = RED;

        // 3. Revise it to a binary search tree
        insertFixUp(node);
    }

    /* 
     * Create a new node (key) and insert it into the red-black tree
     *
     * Description of parameters:
     *     key Key values for inserting nodes
     */
    public void insert(T key) {
        RBTNode<T> node=new RBTNode<T>(key,BLACK,null,null,null);

        // If the new node fails, it returns.
        if (node != null)
            insert(node);
    }


    /*
     * Red-Black Tree Delete Correction Function
     *
     * After deleting the insertion node from the red-black tree (the red-black tree is out of balance), the function is called again.
     * The aim is to reshape it into a red-black tree.
     *
     * Description of parameters:
     *     node Nodes to be amended
     */
    private void removeFixUp(RBTNode<T> node, RBTNode<T> parent) {
        RBTNode<T> other;

        while ((node==null || isBlack(node)) && (node != this.mRoot)) {
            if (parent.left == node) {
                other = parent.right;
                if (isRed(other)) {
                    // Case 1: x's brother w is red  
                    setBlack(other);
                    setRed(parent);
                    leftRotate(parent);
                    other = parent.right;
                }

                if ((other.left==null || isBlack(other.left)) &&
                    (other.right==null || isBlack(other.right))) {
                    // Case 2: x's brother W is black, and w's two children are also black.  
                    setRed(other);
                    node = parent;
                    parent = parentOf(node);
                } else {

                    if (other.right==null || isBlack(other.right)) {
                        // Case 3: x's brother W is black, and w's left child is red, and his right child is black.  
                        setBlack(other.left);
                        setRed(other);
                        rightRotate(other);
                        other = parent.right;
                    }
                    // Case 4: x's brother W is black; and w's right child is red, and his left child is any color.
                    setColor(other, colorOf(parent));
                    setBlack(parent);
                    setBlack(other.right);
                    leftRotate(parent);
                    node = this.mRoot;
                    break;
                }
            } else {

                other = parent.left;
                if (isRed(other)) {
                    // Case 1: x's brother w is red  
                    setBlack(other);
                    setRed(parent);
                    rightRotate(parent);
                    other = parent.left;
                }

                if ((other.left==null || isBlack(other.left)) &&
                    (other.right==null || isBlack(other.right))) {
                    // Case 2: x's brother W is black, and w's two children are also black.  
                    setRed(other);
                    node = parent;
                    parent = parentOf(node);
                } else {

                    if (other.left==null || isBlack(other.left)) {
                        // Case 3: x's brother W is black, and w's left child is red, and his right child is black.  
                        setBlack(other.right);
                        setRed(other);
                        leftRotate(other);
                        other = parent.left;
                    }

                    // Case 4: x's brother W is black; and w's right child is red, and his left child is any color.
                    setColor(other, colorOf(parent));
                    setBlack(parent);
                    setBlack(other.left);
                    rightRotate(parent);
                    node = this.mRoot;
                    break;
                }
            }
        }

        if (node!=null)
            setBlack(node);
    }

    /* 
     * Delete the node and return the deleted node
     *
     * Description of parameters:
     *     node Deleted nodes
     */
    private void remove(RBTNode<T> node) {
        RBTNode<T> child, parent;
        boolean color;

        // The deleted node "left and right children are not empty" situation.
        if ( (node.left!=null) && (node.right!=null) ) {
            // The successor node of the deleted node. (called "replacement node")
            // Use it to replace the "deleted node" position, and then remove the "deleted node".
            RBTNode<T> replace = node;

            // Getting successor nodes
            replace = replace.right;
            while (replace.left != null)
                replace = replace.left;

            // "Node node" is not the root node (only the root node does not have a parent node)
            if (parentOf(node)!=null) {
                if (parentOf(node).left == node)
                    parentOf(node).left = replace;
                else
                    parentOf(node).right = replace;
            } else {
                // The "node node" is the root node, updating the root node.
                this.mRoot = replace;
            }

            // Child is the right child of "replacing node", and also the node that needs to be adjusted.
            // "Replacement Node" definitely does not have a left child! Because it is a successor node.
            child = replace.right;
            parent = parentOf(replace);
            // Save the color of the Replacement Node
            color = colorOf(replace);

            // "Deleted node" is "parent of its successor node"
            if (parent == node) {
                parent = replace;
            } else {
                // child is not empty
                if (child!=null)
                    setParent(child, parent);
                parent.left = child;

                replace.right = node.right;
                setParent(node.right, replace);
            }

            replace.parent = node.parent;
            replace.color = node.color;
            replace.left = node.left;
            node.left.parent = replace;

            if (color == BLACK)
                removeFixUp(child, parent);

            node = null;
            return ;
        }

        if (node.left !=null) {
            child = node.left;
        } else {
            child = node.right;
        }

        parent = node.parent;
        // Save the color of the Replacement Node
        color = node.color;

        if (child!=null)
            child.parent = parent;

        // "Node node" is not the root node
        if (parent!=null) {
            if (parent.left == node)
                parent.left = child;
            else
                parent.right = child;
        } else {
            this.mRoot = child;
        }

        if (color == BLACK)
            removeFixUp(child, parent);
        node = null;
    }

    /* 
     * Delete the node (z) and return the deleted node
     *
     * Description of parameters:
     *     tree Root Node of Red-Black Tree
     *     z Deleted nodes
     */
    public void remove(T key) {
        RBTNode<T> node; 

        if ((node = search(mRoot, key)) != null)
            remove(node);
    }

    /*
     * Destroy red and black trees
     */
    private void destroy(RBTNode<T> tree) {
        if (tree==null)
            return ;

        if (tree.left != null)
            destroy(tree.left);
        if (tree.right != null)
            destroy(tree.right);

        tree=null;
    }

    public void clear() {
        destroy(mRoot);
        mRoot = null;
    }

    /*
     * Print "Red and Black Trees"
     *
     * key        -- Key values of nodes 
     * direction  --  0,Represents that the node is the root node.
     *               -1,Represents that the node is the left child of its parent node;
     *                1,Represents that the node is the right child of its parent node.
     */
    private void print(RBTNode<T> tree, T key, int direction) {

        if(tree != null) {

            if(direction==0)    // tree is the root node
                System.out.printf("%2d(B) is root\n", tree.key);
            else                // tree is a branch node
                System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree)?"R":"B", key, direction==1?"right" : "left");

            print(tree.left, tree.key, -1);
            print(tree.right,tree.key,  1);
        }
    }

    public void print() {
        if (mRoot != null)
            print(mRoot, mRoot.key, 0);
    }
}

RBTreeTest. Java

/**
 * Java Language: Binary Search Tree
 *
 * @author xxx
 * @date 2019/8/13
 */
public class RBTreeTest {

    private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};
    private static final boolean mDebugInsert = false;    // "Insert" action detection switch (false, close; true, open)
    private static final boolean mDebugDelete = false;    // "Delete" action detection switch (false, close; true, open)

    public static void main(String[] args) {
        int i, ilen = a.length;
        RBTree<Integer> tree=new RBTree<Integer>();

        System.out.printf("== Raw data: ");
        for(i=0; i<ilen; i++)
            System.out.printf("%d ", a[i]);
        System.out.printf("\n");

        for(i=0; i<ilen; i++) {
            tree.insert(a[i]);
            // Set mDebugInsert=true to test Add Function
            if (mDebugInsert) {
                System.out.printf("== Adding Nodes: %d\n", a[i]);
                System.out.printf("== Tree details: \n");
                tree.print();
                System.out.printf("\n");
            }
        }

        System.out.printf("== Preorder traversal: ");
        tree.preOrder();

        System.out.printf("\n== Intermediate traversal: ");
        tree.inOrder();

        System.out.printf("\n== Postorder traversal: ");
        tree.postOrder();
        System.out.printf("\n");

        System.out.printf("== minimum value: %s\n", tree.minimum());
        System.out.printf("== Maximum: %s\n", tree.maximum());
        System.out.printf("== Tree details: \n");
        tree.print();
        System.out.printf("\n");

        // Set mDebugDelete=true to test the Delete function
        if (mDebugDelete) {
            for(i=0; i<ilen; i++)
            {
                tree.remove(a[i]);

                System.out.printf("== Delete Nodes: %d\n", a[i]);
                System.out.printf("== Tree details: \n");
                tree.print();
                System.out.printf("\n");
            }
        }

        // Destroying Binary Trees
        tree.clear();
    }
}

Java Testing Program for Red and Black Trees

The test code for red and black trees (RBTreeTest.java) has been given earlier, and I won't repeat it here. The following is the result of the test program:

== Raw data: 10 40 30 60 90 70 20 50 80 
== Preorder traversal: 30 10 20 60 40 50 80 70 90 
== Intermediate traversal: 10 20 30 40 50 60 70 80 90 
== Post-order traversal: 20 10 50 40 70 80 60 30 
== Minimum: 10
 == Maximum: 90
 == Tree details: 
30(B) is root
10(B) is 30's   left child
20(R) is 10's  right child
60(R) is 30's  right child
40(B) is 60's   left child
50(R) is 40's  right child
80(B) is 60's  right child
70(R) is 80's   left child
90(R) is 80's  right child

 

Posted by fifin04 on Fri, 30 Aug 2019 00:34:08 -0700