Question: Select/rank check. Write a method that checks, for all i from 0 to size()-1, whether i is equal to rank(select(i)) and, for all keys in the BST, whether key is equal to select(rank(key)).

Answer:
package chapter3.section2;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 04/06/17.
 */
public class Exercise33_SelectRankCheck {

    private boolean checkRankAndSelect(BinarySearchTree<Integer, String> binarySearchTree) {
        int size = binarySearchTree.size();

        //Check rank
        for (int i = 0; i < size; i++) {
            if (i != binarySearchTree.rank(binarySearchTree.select(i))) {
                return false;
            }
        }

        //Check select
        for (Integer key : binarySearchTree.keys()) {
            if (key.compareTo(binarySearchTree.select(binarySearchTree.rank(key))) != 0) {
                return false;
            }
        }
        return true;
    }

    public static void main(String[] args) {
        BinarySearchTree<Integer, String> binarySearchTree = new BinarySearchTree<>();
        binarySearchTree.put(10, "Value 10");
        binarySearchTree.put(4, "Value 4");
        binarySearchTree.put(6, "Value 6");
        binarySearchTree.put(1, "Value 1");
        binarySearchTree.put(15, "Value 15");
        binarySearchTree.put(12, "Value 12");
        binarySearchTree.put(20, "Value 20");
        binarySearchTree.put(25, "Value 25");

        Exercise33_SelectRankCheck selectRankCheck = new Exercise33_SelectRankCheck();
        StdOut.println(selectRankCheck.checkRankAndSelect(binarySearchTree) + " Expected: true");
    }
}


Question:
Threading. Your goal is to support an extended API ThreadedST that supports the following additional operations in constant time:
Key next(Key key) key that follows key (null if key is the maximum)
Key prev(Key key) key that precedes key (null if key is the minimum)
To do so, add fields pred and succ to Node that contain links to the predecessor and successor nodes, and modify put(), deleteMin(), deleteMax(), and delete() to maintain these fields.

Answer:
package chapter3.section2;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 04/06/17.
 */
@SuppressWarnings("unchecked")
public class Exercise34_Threading {

    private class DoublyThreadedBST<Key extends Comparable<Key>, Value> extends BinarySearchTree<Key, Value> {

        private class Node {

            private Key key;
            private Value value;

            private Node left;
            private Node right;

            private int size; //# of nodes in subtree rooted here

            private Node pred;
            private Node succ;

            public Node(Key key, Value value, int size) {
                this.key = key;
                this.value = value;
                this.size = size;
            }
        }

        private Node root;

        public int size() {
            return size(root);
        }

        private int size(Node node) {
            if (node == null) {
                return 0;
            }

            return node.size;
        }

        public Key next(Key key) {
            if (key == null) {
                return null;
            }

            Node current = root;
            while (current != null) {

                int compare = key.compareTo(current.key);

                if (compare < 0) {
                    current = current.left;
                } else if (compare > 0) {
                    current = current.right;
                } else {
                    if (current.succ != null) {
                        return current.succ.key;
                    } else {
                        return null;
                    }
                }
            }

            return null;
        }

        public Key prev(Key key) {
            if (key == null) {
                return null;
            }

            Node current = root;
            while (current != null) {

                int compare = key.compareTo(current.key);

                if (compare < 0) {
                    current = current.left;
                } else if (compare > 0) {
                    current = current.right;
                } else {
                    if (current.pred != null) {
                        return current.pred.key;
                    } else {
                        return null;
                    }
                }
            }

            return null;
        }

        //Used by delete() method
        private Node min(Node node) {
            if (node.left == null) {
                return node;
            }

            return min(node.left);
        }

        @Override
        public void put(Key key, Value value) {
            root = put(root, key, value, null, null);
        }

        private Node put(Node node, Key key, Value value, Node predecessor, Node successor) {
            if (node == null) {
                Node newNode = new Node(key, value, 1);

                if (predecessor != null) {
                    predecessor.succ = newNode;
                    newNode.pred = predecessor;
                }
                if (successor != null) {
                    newNode.succ = successor;
                    successor.pred = newNode;
                }

                return newNode;
            }

            int compare = key.compareTo(node.key);

            if (compare < 0) {
                node.left = put(node.left, key, value, predecessor, node);
            } else if (compare > 0) {
                node.right = put(node.right, key, value, node, successor);
            } else {
                node.value = value;
            }

            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public void deleteMin() {
            root = deleteMin(root, true);
        }

        //updatePredAndSucc parameter is used because we don't want to update pred and succ fields
        // when using deleteMin() inside delete()
        private Node deleteMin(Node node, boolean updatePredAndSucc) {
            if (node == null) {
                return null;
            }

            if (node.left == null) {
                if (updatePredAndSucc && node.succ != null) {
                    node.succ.pred = null;
                }

                return node.right;
            }

            node.left = deleteMin(node.left, updatePredAndSucc);
            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public void deleteMax() {
            root = deleteMax(root);
        }

        private Node deleteMax(Node node) {
            if (node == null) {
                return null;
            }

            if (node.right == null) {
                if (node.pred != null) {
                    node.pred.succ = null;
                }

                return node.left;
            }

            node.right = deleteMax(node.right);
            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public void delete(Key key) {
            root = delete(root, key);
        }

        private Node delete(Node node, Key key) {
            if (node == null) {
                return null;
            }

            int compare = key.compareTo(node.key);
            if (compare < 0) {
                node.left = delete(node.left, key);
            } else if (compare > 0) {
                node.right = delete(node.right, key);
            } else {
                if (node.left == null || node.right == null) {

                    if (node.pred != null) {
                        node.pred.succ = node.succ;
                    }
                    if (node.succ != null) {
                        node.succ.pred = node.pred;
                    }

                    if (node.left == null) {
                        return node.right;
                    } else if (node.right == null) { //Always true when we get here, but leaving it here for legibility
                        return node.left;
                    }
                } else {
                    Node aux = node;
                    node = min(aux.right);
                    node.right = deleteMin(aux.right, false);
                    node.left = aux.left;

                    node.pred = aux.pred;
                    if (node.pred != null) {
                        node.pred.succ = node;
                    }
                    //The deleted node's successor's (the new root of this subtree) pred and succ fields were already updated
                }
            }

            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

    }

    public static void main(String[] args) {
        Exercise34_Threading threading = new Exercise34_Threading();

        DoublyThreadedBST<Integer, String> binarySearchTree = threading.new DoublyThreadedBST<>();
        binarySearchTree.put(10, "Value 10");
        binarySearchTree.put(4, "Value 4");
        binarySearchTree.put(6, "Value 6");
        binarySearchTree.put(1, "Value 1");
        binarySearchTree.put(2, "Value 2");
        binarySearchTree.put(15, "Value 15");
        binarySearchTree.put(12, "Value 12");
        binarySearchTree.put(20, "Value 20");
        binarySearchTree.put(25, "Value 25");

        //Test put()
        StdOut.println("Predecessor of 1: " + binarySearchTree.prev(1) + " Expected: null");
        StdOut.println("Predecessor of 6: " + binarySearchTree.prev(6) + " Expected: 4");
        StdOut.println("Predecessor of 10: " + binarySearchTree.prev(10) + " Expected: 6");
        StdOut.println("Predecessor of 25: " + binarySearchTree.prev(25) + " Expected: 20");
        StdOut.println("Successor of 1: " + binarySearchTree.next(1) + " Expected: 2");
        StdOut.println("Successor of 6: " + binarySearchTree.next(6) + " Expected: 10");
        StdOut.println("Successor of 12: " + binarySearchTree.next(12) + " Expected: 15");
        StdOut.println("Successor of 25: " + binarySearchTree.next(25) + " Expected: null");

        StdOut.println();

        //Test deleteMin()
        StdOut.println("Predecessor of 2: " + binarySearchTree.prev(2) + " Expected: 1");
        binarySearchTree.deleteMin();
        StdOut.println("Predecessor of 2 after deleteMin(): " + binarySearchTree.prev(2) + " Expected: null");

        StdOut.println();

        //Test deleteMax()
        StdOut.println("Successor of 20: " + binarySearchTree.next(20) + " Expected: 25");
        binarySearchTree.deleteMax();
        StdOut.println("Successor of 20 after deleteMax(): " + binarySearchTree.next(20) + " Expected: null");

        StdOut.println();

        //Test delete()
        StdOut.println("Predecessor of 20: " + binarySearchTree.prev(20) + " Expected: 15");
        StdOut.println("Successor of 12: " + binarySearchTree.next(12) + " Expected: 15");
        binarySearchTree.delete(15);
        StdOut.println("Predecessor of 20 after delete(15): " + binarySearchTree.prev(20) + " Expected: 12");
        StdOut.println("Successor of 12 after delete(15): " + binarySearchTree.next(12) + " Expected: 20");

        StdOut.println();

        StdOut.println("Predecessor of 12: " + binarySearchTree.prev(12) + " Expected: 10");
        StdOut.println("Successor of 6: " + binarySearchTree.next(6) + " Expected: 10");
        binarySearchTree.delete(10);
        StdOut.println("Predecessor of 12 after delete(10): " + binarySearchTree.prev(12) + " Expected: 6");
        StdOut.println("Successor of 6 after delete(10): " + binarySearchTree.next(6) + " Expected: 12");

        StdOut.println();

        StdOut.println("Predecessor of 4: " + binarySearchTree.prev(4) + " Expected: 2");
        binarySearchTree.delete(2);
        StdOut.println("Predecessor of 4 after delete(2): " + binarySearchTree.prev(4) + " Expected: null");

        StdOut.println("Successor of 12: " + binarySearchTree.next(12) + " Expected: 20");
        binarySearchTree.delete(20);
        StdOut.println("Successor of 12 after delete(20): " + binarySearchTree.next(12) + " Expected: null");
    }

}


Question: Refined analysis. Refine the mathematical model to better explain the experimental results in the table given in the text. Specifically, show that the average number of compares for a successful search in a tree built from random keys approaches the limit 2 ln N + 2γ - 3 ≈ 1.39 lg N – 1.85 as N increases, where γ ≈ .57721... is Euler’s constant. Hint: Referring to the quicksort analysis in Section 2.3, use the fact that the integral of 1/x approaches ln N + γ.

Answer:
3.2.35 - Refined analysis

Proposition: The average number of compares for a successful search in a tree built from N random keys approaches the limit 2ln N + 2y - 3 ~= 1.39 lg N - 1.85 as N increases, where y = .57721... is Euler's constant.

Proof:
The number of compares used for a search hit ending at a given node is 1 plus the depth. Adding the depths of all nodes, we get a quantity known as the internal path length of the tree. Thus, the desired quantity is 1 plus the average internal path length of the BST, which we can analyze as follows:
Let Cn be the internal path length of a BST built from inserting N randomly ordered distinct keys, so that the average cost of a search hit is 1 + Cn / N.
We have C0 = C1 = 0 and for N > 1 we can write a recurrence relationship that directly mirrors the recursive BST structure:
Cn = N - 1 + (C0 + Cn-1) / N + (C1 + Cn-2) / N + ... + (Cn-1 + C0) / N
The N - 1 term takes into account that the root contributes 1 to the path length of each of the other N - 1 nodes in the tree; the rest of the expression accounts for the subtrees, which are equally likely to be any of the N sizes.

Multiplying by N and collecting terms transforms this equation into:
NCn = N(N - 1) + 2(C0 + C1 + ... + Cn-2 + Cn-1)
Subtracting the same equation for N - 1 from this equation gives
NCn - (N - 1)Cn-1 = 2N - 2 + 2Cn-1
Rearranging terms and dividing by N(N + 1) leaves
Cn / (N + 1) = Cn-1 / N + 2 / (N + 1) - 2 / (N (N + 1))
which telescopes to give the result
Cn ~ 2(N + 1) * (1/3 + 1/4 + ... + 1/(N + 1) - 2)

The parenthesized quantity is the discrete estimate of the area under the curve 1/x from 3 to N + 1. By integration, 1/x approaches ln N + y.
So we have:
Cn ~ 2(N + 1) * (ln N + y - 2)
Cn ~ (2N + 2) * (ln N + y - 2)
Cn ~ 2N ln N + 2N y - 4N + 2ln N + 2y - 4

As mentioned before, the average cost of a search hit is:
AVG Cost of Search Hit = 1 + Cn / N

Replacing Cn with 2N ln N + 2N y - 4N + 2ln N + 2y - 4 we have:
AVG Cost of Search Hit = 1 + (2N ln N + 2N y - 4N + 2ln N + 2y - 4) / N
AVG Cost of Search Hit = 1 + 2 ln N + 2y + 2y/N - 4 - 4/N

Since 2 ln N ~ 1.39 lg N this gives the following approximation as N increases
AVG Cost of Search Hit ~ 1.39 lg N + 2y - 3
Replacing y with .57721
AVG Cost of Search Hit ~ 1.39 lg N + 1.154 - 3
AVG Cost of Search Hit ~ 1.39 lg N - 1.85


Question: Iterator. Is it possible to write a nonrecursive version of keys() that uses space proportional to the tree height (independent of the number of keys in the range)?

Answer:
3.2.36 - Iterator

Yes, it is possible to write a nonrecursive version of keys() that uses space proportional to the tree height (independent of the number of keys in the range).

This can be done using a stack.
1- Add the root of the tree and all of its left children on the stack (as long as they are in the searched range).
2- If the stack is not empty: Pop the top element, print it, set the current element as the printed element's right child (if it is not null -if it is, repeat this step).
3- Add the right child (if it is in the searched range) and all of its left children (as long as they are in the searched range) on the stack.
4- Repeat steps 2 and 3 until the stack is empty or all the range has been printed.


Question: Level-order traversal. Write a method printLevel() that takes a Node as argument and prints the keys in the subtree rooted at that node in level order (in order of their distance from the root, with nodes on each level in order from left to right). Hint: Use a Queue.

Answer:
package chapter3.section2;

import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 10/06/17.
 */
public class Exercise37_LevelOrderTraversal {

    private void printLevel(BinarySearchTree.Node node) {
        Queue<BinarySearchTree.Node> queue = new Queue<>();
        queue.enqueue(node);

        while (!queue.isEmpty()) {
            BinarySearchTree.Node current = queue.dequeue();
            StdOut.print(current.key + " ");

            if (current.left != null) {
                queue.enqueue(current.left);
            }
            if (current.right != null) {
                queue.enqueue(current.right);
            }
        }
    }

    public static void main(String[] args) {
        Exercise37_LevelOrderTraversal levelOrderTraversal = new Exercise37_LevelOrderTraversal();

        /**
         *       10
         *   4       15
         * 1   6   12  20
         *  2            25
         */

        BinarySearchTree<Integer, String> binarySearchTree = new BinarySearchTree<>();
        binarySearchTree.put(10, "Value 10");
        binarySearchTree.put(4, "Value 4");
        binarySearchTree.put(6, "Value 6");
        binarySearchTree.put(1, "Value 1");
        binarySearchTree.put(2, "Value 2");
        binarySearchTree.put(15, "Value 15");
        binarySearchTree.put(12, "Value 12");
        binarySearchTree.put(20, "Value 20");
        binarySearchTree.put(25, "Value 25");

        levelOrderTraversal.printLevel(binarySearchTree.root);
        StdOut.println("\nExpected: 10 4 15 1 6 12 20 2 25");
    }

}


Question:
Find the probability that each of the 2-3 trees in Exercise 3.3.5 is the result of the insertion of N random distinct keys into an initially empty tree.

Exercise 3.3.5 trees:

```text
3.3.5

N = 7

    ( )
 () ( ) ( )

     ()
  ()    ()
() () () ()

N = 8

    ( )
( ) ( ) ( )

     ()
  ()    ()
() () () ( )

N = 9

      ()
   ()    ()
( ) ( ) () ()

     ()
  ()    ( )
() () () () ()

N = 10

       ()
   ()     ()
( ) ( ) ( ) ()

        ()
    ( )    ()
( ) () () () ()

       ()
   ()      ( )
( )  ()  () () ()
```

Answer:
3.3.6

N = 7

    ( )
 () ( ) ( )

Probability of this tree being the result: 4 / 7 = 0.57

     ()
  ()    ()
() () () ()

Probability of this tree being the result: 3 / 7 = 0.43

N = 8

    ( )
( ) ( ) ( )

Probability of this tree being the result: (4 / 7) * (2 / 8) = 1 / 7 = 0.14

     ()
  ()    ()
() () () ( )

Probability of this tree being the result: (4 / 7) * (6 / 8) + 3 / 7 = 6 / 7 = 0.86

N = 9

      ()
   ()    ()
( ) ( ) () ()

Probability of this tree being the result: (1 / 7) * (6 / 9) + (6 / 7) * (2 / 9) = 2 / 7 = 0.29

      ()
   ()    ()
( ) () () ( )

Probability of this tree being the result: (1 / 7) * (3 / 9) + (6 / 7) * (4 / 9) = 3 / 7 = 0.43

     ()
  ()    ( )
() () () () ()

Probability of this tree being the result: (6 / 7) * (3 / 9) = 2 / 7 = 0.29

N = 10

       ()
   ()      ()
( ) ( )  ( ) ()

Probability of this tree being the result: (2 / 7) * (4 / 10) + (3 / 7) * (4 / 10) = 2 / 7 = 0.29

        ()
    ( )     ()
( ) () ()  () ()

Probability of this tree being the result: (2 / 7) * (6 / 10) + (2 / 7) * (6 / 10) = 0.34

        ()
   ( )      ()
() () ()  ( ) ()

Probability of this tree being the result: (3 / 7) * (6 / 10) + (2 / 7) * (4 / 10) = 0.37

// Thanks to Oreshnik (https://github.com/Oreshnik) for mentioning more tree structures and fixing an issue in the probability computation for trees with N = 9.
// https://github.com/reneargento/algorithms-sedgewick-wayne/pull/74


Question:
Draw diagrams like the one at the top of page 428 for the other five cases in the bottom diagram on that page. Use keys in sorted order `a < b < c < d < e`, and cover these insertion cases:

1. insertion into the root 3-node,
2. insertion into the left child of a 2-node parent,
3. insertion into the right child of a 2-node parent,
4. insertion into the left child of a 3-node parent,
5. insertion into the middle child of a 3-node parent,
6. insertion into the right child of a 3-node parent.

Answer:
3.3.7

1- root
                                 abc
less than a   between a and b   between b and c   greater than c

                                  b
            a                                   c
less than a   between a and b   between b and c   greater than c

2- parent is a 2-node (left)
                                                 d
                                 abc                             greater than d
less than a   between a and b   between b and c   between c and d

                                                 bd
            a                                   c                             greater than d
less than a   between a and b   between b and c   between c and d

3- parent is a 2-node (right)
                                       a
                 less than a                              bcd
                                between a and b   between b and c   between c and d   greater than d

                                       ac
                 less than a                    b                                   d
                                between a and b   between b and c   between c and d   greater than d

4- parent is a 3-node (left)
                                                 de
                                 abc                 between d and e                 greater than e
less than a   between a and b   between b and c   between c and d

                                               bde
            a                                   c                 between d and e                 greater than e
less than a   between a and b   between b and c   between c and d

5- parent is a 3-node (right)
                                       ab
                 less than a    between a and b           cde
                                                  between b and c   between c and d   between d and e   greater than e

                                                  abd
                 less than a    between a and b                   c                                   e
                                                  between b and c   between c and d   between d and e   greater than e


Question: Show all possible ways that one might represent a 4-node with three 2-nodes bound together with red links (not necessarily left-leaning).

Answer:
3.3.8

Unique representations (not considering the order of the nodes):

   A
    B
     C

   A
    C
   B

   B
  A C

   C
  A
   B

    C
   B
  A


Question:
Which of the following candidate trees are red-black BSTs?

The candidates are the four textbook diagrams (i)–(iv) from Exercise 3.3.9 (Sedgewick & Wayne, Section 3.3). Identify which are valid red-black BSTs and briefly justify your answer.

Answer:
3.3.9

(iii) and (iv)

(i) is not balanced
(ii) is not ordered (F cannot be on the left side of E)


Question: Draw the red-black BST that results when you insert items with the keys E A S Y Q U T I O N in that order into an initially empty tree.

Answer:
3.3.10

Insert E        (B)E

       A        (B)E
             (R)A

       S        (B)E
             (R)A   (R)S

       S        (R)E
             (B)A   (B)S

       S        (B)E
             (B)A   (B)S

       Y        (B)E
             (B)A   (B)S
                      (R)Y

       Y          (B)E
             (B)A     (B)Y
                    (R)S

       Q          (B)E
             (B)A      (B)Y
                     (R)S
                   (R)Q

       Q          (B)E
             (B)A      (B)S
                     (R)Q  (R)Y

       Q          (B)E
             (B)A      (R)S
                     (B)Q  (B)Y

       Q          (B)S
             (R)E      (B)Y
          (B)A  (B)Q

       U           (B)S
             (R)E       (B)Y
          (B)A  (B)Q  (R)U

       T            (B)S
             (R)E         (B)Y
          (B)A  (B)Q    (R)U
                      (R)T

       T           (B)S
             (R)E        (B)U
          (B)A  (B)Q   (R)T  (R)Y

       T           (B)S
             (R)E        (R)U
          (B)A  (B)Q   (B)T  (B)Y

       T           (R)S
             (B)E        (B)U
          (B)A  (B)Q   (B)T  (B)Y

       T           (B)S
             (B)E        (B)U
          (B)A  (B)Q   (B)T  (B)Y

       I             (B)S
              (B)E          (B)U
          (B)A    (B)Q   (B)T  (B)Y
                (R)I

       O             (B)S
              (B)E          (B)U
          (B)A    (B)Q   (B)T  (B)Y
                (R)I
                  (R)O

       O             (B)S
              (B)E          (B)U
          (B)A    (B)Q   (B)T  (B)Y
                 (R)O
               (R)I

       O               (B)S
              (B)E             (B)U
          (B)A    (B)O      (B)T  (B)Y
                (R)I (R)Q

       O               (B)S
              (B)E             (B)U
          (B)A    (R)O      (B)T  (B)Y
                (B)I (B)Q

       O               (B)S
               (B)O             (B)U
           (R)E    (B)Q      (B)T  (B)Y
        (B)A  (B)I

       N               (B)S
               (B)O             (B)U
           (R)E    (B)Q      (B)T  (B)Y
        (B)A  (B)I
                 (R)N

       N                (B)S
                (B)O             (B)U
           (R)E     (B)Q      (B)T  (B)Y
        (B)A  (B)N
             (R)I


Question: Draw the red-black BST that results when you insert items with the keys Y L P M X H C R A E S in that order into an initially empty tree.

Answer:
3.3.11

Insert Y        (B)Y

       L        (B)Y
             (R)L

       P        (B)Y
             (R)L
                (R)P

       P        (B)Y
             (R)P
           (R)L

       P      (B)P
           (R)L  (R)Y

       P      (R)P
           (B)L  (B)Y

       P      (B)P
           (B)L  (B)Y

       M        (B)P
           (B)L      (B)Y
             (R)M

       M       (B)P
           (B)M     (B)Y
         (R)L

       X        (B)P
           (B)M      (B)Y
         (R)L      (R)X

       H         (B)P
            (B)M      (B)Y
          (R)L      (R)X
         (R)H

       H           (B)P
            (B)L         (B)Y
          (R)H (R)M    (R)X

       H           (B)P
            (R)L         (B)Y
          (B)H (B)M    (R)X

       C            (B)P
             (R)L         (B)Y
           (B)H (B)M    (R)X
         (R)C

       R            (B)P
             (R)L         (B)Y
           (B)H (B)M    (R)X
         (R)C          (R)R

       R            (B)P
             (R)L         (B)X
           (B)H (B)M    (R)R (R)Y
         (R)C

       R            (B)P
             (R)L         (R)X
           (B)H (B)M    (B)R (B)Y
         (R)C

       R            (R)P
             (B)L         (B)X
           (B)H (B)M    (B)R (B)Y
         (R)C

       R            (B)P
             (B)L         (B)X
           (B)H (B)M    (B)R (B)Y
         (R)C

       A              (B)P
               (B)L         (B)X
             (B)H (B)M    (B)R (B)Y
           (R)C
          (R)A

       A                 (B)P
                  (B)L          (B)X
             (B)C     (B)M    (B)R (B)Y
           (R)A (R)H

       A                 (B)P
                  (B)L          (B)X
             (R)C     (B)M    (B)R (B)Y
           (B)A (B)H

       E                 (B)P
                   (B)L          (B)X
              (R)C     (B)M    (B)R (B)Y
           (B)A  (B)H
                (R)E

       S                 (B)P
                   (B)L             (B)X
              (R)C     (B)M    (B)R     (B)Y
           (B)A  (B)H             (R)S
                (R)E

       S                 (B)P
                   (B)L             (B)X
              (R)C     (B)M     (B)S    (B)Y
           (B)A  (B)H         (R)R
                (R)E


Question: True or false: If you insert keys in increasing order into a red-black BST, the tree height is monotonically increasing.

Answer:
3.3.13

True.

The following visualization shows 255 keys inserted into a red-black BST in ascending order.
[visualization](https://algs4.cs.princeton.edu/33balanced/media/red-black-255ascending.mov)
from
[source](https://algs4.cs.princeton.edu/33balanced/)


Question: Draw the red-black BST that results when you insert letters A through K in order into an initially empty tree, then describe what happens in general when trees are built by insertion of keys in ascending order (see also the figure in the text).

Answer:
3.3.14

Insert A        (B)A

       B        (B)A
                   (R)B

       B        (B)B
              (R)A

       C         (B)B
              (R)A  (R)C

       C         (R)B
              (B)A  (B)C

       C         (B)B
              (B)A  (B)C

       D         (B)B
              (B)A  (B)C
                      (R)D

       D          (B)B
              (B)A    (B)D
                    (R)C

       E          (B)B
              (B)A     (B)D
                    (R)C  (R)E

       E          (B)B
              (B)A     (R)D
                    (B)C  (B)E

       E            (B)D
               (R)B     (B)E
            (B)A  (B)C

       F            (B)D
               (R)B     (B)E
            (B)A  (B)C     (R)F

       F             (B)D
               (R)B        (B)F
            (B)A  (B)C  (R)E

       G             (B)D
               (R)B        (B)F
            (B)A  (B)C  (R)E  (R)G

       G             (B)D
               (R)B        (R)F
            (B)A  (B)C  (B)E  (B)G

       G             (R)D
               (B)B        (B)F
            (B)A  (B)C  (B)E  (B)G

       G             (B)D
               (B)B        (B)F
            (B)A  (B)C  (B)E  (B)G

       H             (B)D
               (B)B        (B)F
            (B)A  (B)C  (B)E  (B)G
                                (R)H

       H             (B)D
               (B)B         (B)F
            (B)A  (B)C  (B)E    (B)H
                              (R)G

       I             (B)D
               (B)B         (B)F
            (B)A  (B)C  (B)E    (B)H
                              (R)G  (R)I

       I             (B)D
               (B)B         (B)F
            (B)A  (B)C  (B)E    (R)H
                              (B)G  (B)I

       I               (B)D
               (B)B             (B)H
            (B)A  (B)C     (R)F     (B)I
                        (B)E  (B)G

       J               (B)D
               (B)B             (B)H
            (B)A  (B)C     (R)F     (B)I
                        (B)E  (B)G    (R)J

       J                (B)D
               (B)B              (B)H
            (B)A  (B)C     (R)F       (B)J
                        (B)E  (B)G  (R)I

       K                (B)D
               (B)B              (B)H
            (B)A  (B)C     (R)F        (B)J
                        (B)E  (B)G  (R)I  (R)K

       K                (B)D
               (B)B              (B)H
            (B)A  (B)C     (R)F        (R)J
                        (B)E  (B)G  (B)I  (B)K

       K                (B)D
               (B)B              (R)H
            (B)A  (B)C     (B)F        (B)J
                        (B)E  (B)G  (B)I  (B)K

       K                    (B)H
                     (R)D             (B)J
              (B)B        (B)F     (B)I  (B)K
           (B)A  (B)C  (B)E  (B)G

Every insert operation requires either coloring (zero or more operations), left rotation (at most one) or a combination of coloring and left rotation.
The more nodes in the tree, the more nodes involved in these operations.
The tree height is monotonically increasing when inserting keys in increasing order.


Question: For left-leaning red-black BSTs, answer Exercises 3.3.13 and 3.3.14 for the case when the keys are inserted in descending order: show the rotations/color flips and state whether the tree height is monotonically increasing.

Answer:
3.3.15

If keys are inserted in decreasing order into a red-black BST, the tree height is NOT monotonically increasing.

Insert K        (B)K

Insert J        (B)K
              (R)J

Insert I         (B)K
              (R)J
            (R)I

Insert I         (B)J
              (R)I  (R)K

Insert I         (R)J
              (B)I  (B)K

Insert I         (B)J
              (B)I  (B)K

Insert H         (B)J
              (B)I  (B)K
            (R)H

Insert G         (B)J
              (B)I  (B)K
            (R)H
          (R)G

Insert G            (B)J
               (B)H      (B)K
            (R)G  (R)I

Insert G            (B)J
               (R)H      (B)K
            (B)G  (B)I

Insert F            (B)J
               (R)H      (B)K
            (B)G  (B)I
          (R)F

Insert E             (B)J
                (R)H      (B)K
             (B)G  (B)I
           (R)F
         (R)E

Insert E                (B)J
                   (R)H      (B)K
              (B)F     (B)I
           (R)E  (R)G

Insert E                (B)J
                   (R)H      (B)K
              (R)F     (B)I
           (B)E  (B)G

Insert E                (B)H
                  (R)F        (R)J
              (B)E  (B)G   (B)I  (B)K

Insert E                (R)H
                  (B)F        (B)J
              (B)E  (B)G   (B)I  (B)K

Insert E                (B)H
                  (B)F        (B)J
              (B)E  (B)G   (B)I  (B)K     (Tree height decreased from 3 to 2)

Insert D                (B)H
                  (B)F        (B)J
              (B)E  (B)G   (B)I  (B)K
            (R)D

Insert C                (B)H
                  (B)F        (B)J
              (B)E  (B)G   (B)I  (B)K
            (R)D
          (R)C

Insert C                  (B)H
                   (B)F         (B)J
              (B)D    (B)G   (B)I  (B)K
            (R)C (R)E

Insert C                  (B)H
                   (B)F         (B)J
              (R)D    (B)G   (B)I  (B)K
            (B)C (B)E

Insert B                  (B)H
                   (B)F         (B)J
              (R)D    (B)G   (B)I  (B)K
            (B)C (B)E
          (R)B

Insert A                   (B)H
                    (B)F         (B)J
               (R)D    (B)G   (B)I  (B)K
             (B)C (B)E
           (R)B
         (R)A

Insert A                      (B)H
                       (B)F          (B)J
                  (R)D     (B)G   (B)I  (B)K
              (B)B   (B)E
           (R)A (R)C

Insert A                      (B)H
                       (B)F          (B)J
                  (R)D     (B)G   (B)I  (B)K
              (R)B   (B)E
           (B)A (B)C

Insert A                     (B)H
                     (B)D            (B)J
               (R)B       (R)F    (B)I  (B)K
            (B)A (B)C   (B)E (B)G

Insert A                     (B)H
                     (R)D            (B)J
               (B)B       (B)F    (B)I  (B)K
            (B)A (B)C   (B)E (B)G             (Tree height decreased from 4 to 3)

The insert operation may require no other operations, coloring, right rotation (zero, one or two) or a combination of coloring and right rotation.
The more nodes in the tree, the more nodes involved in these operations.
The tree height is not monotonically increasing when inserting keys in decreasing order.


Question: Draw all the structurally different red-black BSTs with N keys, for N from 2 up to 10 (see Exercise 3.3.5).

Answer:
3.3.18

N = 2

  (B)N
(R)N

N = 3

   (B)N
(B)N (B)N

N = 4

     (B)N
(B)N      (B)N
        (R)N

N = 5

       (B)N
  (B)N      (B)N
(R)N      (R)N

       (B)N
  (R)N      (B)N
(B)N (B)N

N = 6

        (B)N
  (R)N       (B)N
(B)N (B)N  (R)N

         (B)N
    (R)N      (B)N
  (B)N (B)N
(R)N

N = 7

           (B)N
    (R)N          (B)N
(B)N    (B)N    (R)N
      (R)N

        (B)N
  (B)N        (B)N
(B)N (B)N  (B)N  (B)N

          (B)N
     (R)N      (B)N
  (B)N   (B)N
(R)N   (R)N

N = 8

              (B)N
       (R)N          (B)N
   (B)N    (B)N    (R)N
(R)N     (R)N

        (B)N
  (B)N         (B)N
(B)N (B)N  (B)N    (B)N
                 (R)N

N = 9

              (B)N
       (B)N           (B)N
   (B)N    (B)N    (B)N  (B)N
(R)N     (R)N

          (B)N
  (B)N            (B)N
(B)N (B)N    (R)N     (B)N
          (B)N  (B)N

N = 10

               (B)N
       (B)N            (B)N
   (B)N    (B)N     (B)N  (B)N
(R)N     (R)N     (R)N

                (B)N
         (B)N            (B)N
     (R)N    (B)N     (B)N  (B)N
  (B)N  (B)N
(R)N

                (B)N
        (B)N           (B)N
   (R)N      (B)N    (B)N  (B)N
B(N)  B(N) (R)N

Thanks to QiotoF (https://github.com/QiotoF) for mentioning a missing structure for N = 6.
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/103


Question: Compute the internal path length in a perfectly balanced BST of N nodes, when N is a power of 2 minus 1.

Answer:
3.3.20

For a perfectly balanced BST of N nodes, when N is a power of 2 minus 1, the internal path lengths are:

P = 1 -> 2^1 - 1 = 1 node -> Internal path length: 0 -> Which is 2^0 * 0
P = 2 -> 2^2 - 1 = 3 nodes -> Internal path length: 2 -> Which is 2^0 * 0 + 2^1 * 1
P = 3 -> 2^3 - 1 = 7 nodes -> Internal path length: 10 -> Which is 2^0 * 0 + 2^1 * 1 + 2^2 * 2
P = 4 -> 2^4 - 1 = 15 nodes -> Internal path length: 34 -> Which is 2^0 * 0 + 2^1 * 1 + 2^2 * 2 + 2^3 * 3

                         h
Internal path length =  SUM 2^i * i
                        i=0

Where h is the height of the tree.

The terms 2^i * i are an arithmetico-geometric sequence, and using the technique of telescoping series, their sum has
a closed-form expression.
Reference: https://en.wikipedia.org/wiki/Arithmetico%E2%80%93geometric_sequence

Following the description from the above reference, for the sum of the terms we have:
a = 1
b = 2
d = 1
r = 2

A1 = 1
G1 = 2
A(n + 1) = h + 1
G(n + 1) = 2^(h + 1)

Closed-form expression:
Sn = (A1 * G1 - (A(n + 1) * G(n+ 1))) / (1 - r) + (d * r) / (1 - r)^2 * (G1 - G(n + 1))

Replacing the values:
Sn = (1 * 2 - ((h + 1) * 2^(h + 1))) / (1 - 2) + (1 * 2) / (1 - 2)^2 * (2 - 2^(h + 1))
Sn = (2 - ((h + 1) * 2^(h + 1))) / (-1) + 2 * (2 - 2^(h + 1))
Sn = (2 - (h * 2^(h + 1) + 2^(h + 1))) / (-1) + 4 - 2 * 2^(h + 1)
Sn = -2 + h * 2^(h + 1) + 2^(h + 1) + 4 - 2 * 2^(h + 1)
Sn = 2 + h * 2^(h + 1) + (-1) * 2^(h + 1)
Sn = 2 + (h - 1) * 2^(h + 1)

Replacing h with N we have:
N = 2^(h + 1) - 1
h = lg(N + 1) - 1

Sn = 2 + ((lg(N + 1) - 1) - 1) * 2^((lg(N + 1) - 1) + 1)
Sn = 2 + (lg(N + 1) - 2) * 2^(lg(N + 1))
Sn = 2 + (lg(N + 1) - 2) * (N + 1)
Sn = 2 + lg(N + 1) * (N + 1) - 2 * (N + 1)
Sn = 2 + lg(N + 1) * (N + 1) - 2 * N - 2
Sn = lg(N + 1) * (N + 1) - 2 * N

Internal path length = lg(N + 1) * (N + 1) - 2 * N

Where N is the number of nodes in the tree.

Thanks to luowyang (https://github.com/luowyang) for mentioning about the closed-form expression and precise solution
to this exercise:
https://github.com/reneargento/algorithms-sedgewick-wayne/issues/94


Question: Create a test client TestRB.java, based on your solution to Exercise 3.2.10.

Answer:
package chapter3.section3;

import edu.princeton.cs.algs4.StdIn;
import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 19/06/17.
 */
public class Exercise21 {

    public static void main(String[] args) {

        /** Test type
         * 0- Keys()
         * 1- Min()
         * 2- Max()
         * 3- Floor()
         * 4- Ceiling()
         * 5- Select()
         * 6- Rank()
         * 7- Delete()
         * 8- DeleteMin()
         * 9- DeleteMax()
         * 10- Keys() with range
         *
         * Or no value, for all tests
         */

        int testType = -1;
        if (args.length == 1) {
            testType = Integer.parseInt(args[0]);
        }

        //Test values:
        //5 1 9 2 0 99

        //Expected tree
        //       (B)5
        //  (R)1      (B)99
        //(B)0 (B)2  (R)9

        RedBlackBST<Integer, String> redBlackBST = new RedBlackBST<>();

        while (!StdIn.isEmpty()) {
            Integer key = StdIn.readInt();
            redBlackBST.put(key, "Value " + key);
        }

        if (testType == -1 || testType == 0) {
            StdOut.println("Keys() test");

            for (Integer key : redBlackBST.keys()) {
                StdOut.println("Key " + key + ": " + redBlackBST.get(key));
            }

            StdOut.println("Expected: 0 1 2 5 9 99\n");
        }

        if (testType == -1 || testType == 1) {
            // Test min()
            StdOut.println("Min key: " + redBlackBST.min() + " Expected: 0");
        }

        if (testType == -1 || testType == 2) {
            // Test max()
            StdOut.println("Max key: " + redBlackBST.max() + " Expected: 99");
        }

        if (testType == -1 || testType == 3) {
            // Test floor()
            StdOut.println("Floor of 5: " + redBlackBST.floor(5) + " Expected: 5");
            StdOut.println("Floor of 15: " + redBlackBST.floor(15) + " Expected: 9");
        }

        if (testType == -1 || testType == 4) {
            // Test ceiling()
            StdOut.println("Ceiling of 5: " + redBlackBST.ceiling(5) + " Expected: 5");
            StdOut.println("Ceiling of 15: " + redBlackBST.ceiling(15) + " Expected: 99");
        }

        if (testType == -1 || testType == 5) {
            // Test select()
            StdOut.println("Select key of rank 4: " + redBlackBST.select(4) + " Expected: 9");
        }

        if (testType == -1 || testType == 6) {
            // Test rank()
            StdOut.println("Rank of key 9: " + redBlackBST.rank(9) + " Expected: 4");
            StdOut.println("Rank of key 10: " + redBlackBST.rank(10) + " Expected: 5");
        }

        if (testType == -1 || testType == 7) {
            // Test delete()
            StdOut.println("\nDelete key 2");
            redBlackBST.delete(2);

            for (Integer key : redBlackBST.keys()) {
                StdOut.println("Key " + key + ": " + redBlackBST.get(key));
            }
        }

        if (testType == -1 || testType == 8) {
            // Test deleteMin()
            StdOut.println("\nDelete min (key 0)");
            redBlackBST.deleteMin();

            for (Integer key : redBlackBST.keys()) {
                StdOut.println("Key " + key + ": " + redBlackBST.get(key));
            }
        }

        if (testType == -1 || testType == 9) {
            // Test deleteMax()
            StdOut.println("\nDelete max (key 99)");
            redBlackBST.deleteMax();

            for (Integer key : redBlackBST.keys()) {
                StdOut.println("Key " + key + ": " + redBlackBST.get(key));
            }
        }

        if (testType == -1 || testType == 10) {
            // Test keys() with range
            StdOut.println("\nKeys in range [2, 10]");
            for (Integer key : redBlackBST.keys(2, 10)) {
                StdOut.println("Key " + key + ": " + redBlackBST.get(key));
            }
        }
    }
}


Question: 2-3 trees without balance restriction. Develop an implementation of the basic symbol-table API that uses 2-3 trees that are not necessarily balanced as the underlying data structure. Allow 3-nodes to lean either way. Hook the new node onto the bottom with a black link when inserting into a 3-node at the bottom. Run experiments to develop a hypothesis estimating the average path length in a tree built from N random insertions.

Answer:
// Exercise23_23TreesWithoutBalance.java
package chapter3.section3;

import edu.princeton.cs.algs4.Queue;
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;

import java.util.NoSuchElementException;

/**
 * Created by Rene Argento on 24/06/17.
 */
public class Exercise23_23TreesWithoutBalance {

    private class TwoThreeTreeNonBalanced<Key extends Comparable<Key>, Value> {

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

        private class Node {
            Key key;
            Value value;
            Node left, right;

            boolean color;
            int size;

            private int depth; //used only to compute the internal path length

            Node(Key key, Value value, int size, boolean color) {
                this.key = key;
                this.value = value;

                this.size = size;
                this.color = color;
            }
        }

        private Node root;

        public int size() {
            return size(root);
        }

        private int size(Node node) {
            if (node == null) {
                return 0;
            }

            return node.size;
        }

        public boolean isEmpty() {
            return size(root) == 0;
        }

        private boolean isRed(Node node) {
            if (node == null) {
                return false;
            }

            return node.color == RED;
        }

        private Node rotateLeft(Node node) {
            if (node == null || node.right == null) {
                return node;
            }

            Node newRoot = node.right;

            node.right = newRoot.left;
            newRoot.left = node;

            newRoot.color = node.color;
            node.color = RED;

            newRoot.size = node.size;
            node.size = size(node.left) + 1 + size(node.right);

            return newRoot;
        }

        private Node rotateRight(Node node) {
            if (node == null || node.left == null) {
                return node;
            }

            Node newRoot = node.left;

            node.left = newRoot.right;
            newRoot.right = node;

            newRoot.color = node.color;
            node.color = RED;

            newRoot.size = node.size;
            node.size = size(node.left) + 1 + size(node.right);

            return newRoot;
        }

        private void flipColors(Node node) {
            if (node == null || node.left == null || node.right == null) {
                return;
            }

            //The root must have opposite color of its two children
            if ((isRed(node) && !isRed(node.left) && !isRed(node.right))
                    || (!isRed(node) && isRed(node.left) && isRed(node.right))) {
                node.color = !node.color;
                node.left.color = !node.left.color;
                node.right.color = !node.right.color;
            }
        }

        public void put(Key key, Value value) {
            if (key == null) {
                return;
            }

            if (value == null) {
                delete(key);
                return;
            }

            root = put(root, key, value, root);
            root.color = BLACK;
        }

        private Node put(Node node, Key key, Value value, Node parent) {
            if (node == null) {
                boolean isTwoThreeNode = true;

                //If the parent is red, it is a 3-node
                //Otherwise, check the node sibling's color
                if (parent != null && !isRed(parent)) {
                    boolean isLeftChild = key.compareTo(parent.key) < 0;

                    if (isLeftChild) {
                        if (!isRed(parent.right)) {
                            isTwoThreeNode = false;
                        }
                    } else {
                        if (!isRed(parent.left)) {
                            isTwoThreeNode = false;
                        }
                    }
                }

                return new Node(key, value, 1, !isTwoThreeNode);
            }

            int compare = key.compareTo(node.key);

            if (compare < 0) {
                node.left = put(node.left, key, value, node);
            } else if (compare > 0) {
                node.right = put(node.right, key, value, node);
            } else {
                node.value = value;
            }

            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public Value get(Key key) {
            if (key == null) {
                return null;
            }

            return get(root, key);
        }

        private Value get(Node node, Key key) {
            if (node == null) {
                return null;
            }

            int compare = key.compareTo(node.key);
            if (compare < 0) {
                return get(node.left, key);
            } else if (compare > 0) {
                return get(node.right, key);
            } else {
                return node.value;
            }
        }

        public boolean contains(Key key) {
            if (key == null) {
                throw new IllegalArgumentException("Argument to contains() cannot be null");
            }
            return get(key) != null;
        }

        public Key min() {
            if (root == null) {
                throw new NoSuchElementException("Empty binary search tree");
            }

            return min(root).key;
        }

        protected Node min(Node node) {
            if (node.left == null) {
                return node;
            }

            return min(node.left);
        }

        public Key max() {
            if (root == null) {
                throw new NoSuchElementException("Empty binary search tree");
            }

            return max(root).key;
        }

        private Node max(Node node) {
            if (node.right == null) {
                return node;
            }

            return max(node.right);
        }

        //Returns the highest key in the symbol table smaller than or equal to key.
        public Key floor(Key key) {
            Node node = floor(root, key);
            if (node == null) {
                return null;
            }

            return node.key;
        }

        private Node floor(Node node, Key key) {
            if (node == null) {
                return null;
            }

            int compare = key.compareTo(node.key);

            if (compare == 0) {
                return node;
            } else if (compare < 0) {
                return floor(node.left, key);
            } else {
                Node rightNode = floor(node.right, key);
                if (rightNode != null) {
                    return rightNode;
                } else {
                    return node;
                }
            }
        }

        //Returns the smallest key in the symbol table greater than or equal to key.
        public Key ceiling(Key key) {
            Node node = ceiling(root, key);
            if (node == null) {
                return null;
            }

            return node.key;
        }

        private Node ceiling(Node node, Key key) {
            if (node == null) {
                return null;
            }

            int compare = key.compareTo(node.key);

            if (compare == 0) {
                return node;
            } else if (compare > 0) {
                return ceiling(node.right, key);
            } else {
                Node leftNode = ceiling(node.left, key);
                if (leftNode != null) {
                    return leftNode;
                } else {
                    return node;
                }
            }
        }

        public Key select(int index) {
            if (index >= size()) {
                throw new IllegalArgumentException("Index is higher than tree size");
            }

            return select(root, index).key;
        }

        private Node select(Node node, int index) {
            int leftSubtreeSize = size(node.left);

            if (leftSubtreeSize == index) {
                return node;
            } else if (leftSubtreeSize > index) {
                return select(node.left, index);
            } else {
                return select(node.right, index - leftSubtreeSize - 1);
            }
        }

        public int rank(Key key) {
            return rank(root, key);
        }

        private int rank(Node node, Key key) {
            if (node == null) {
                return 0;
            }

            //Returns the number of keys less than node.key in the subtree rooted at node
            int compare = key.compareTo(node.key);
            if (compare < 0) {
                return rank(node.left, key);
            } else if (compare > 0) {
                return size(node.left) + 1 + rank(node.right, key);
            } else {
                return size(node.left);
            }
        }

        public void deleteMin() {
            if (isEmpty()) {
                return;
            }

            if (!isRed(root.left) && !isRed(root.right)) {
                root.color = RED;
            }

            root = deleteMin(root);

            if (!isEmpty()) {
                root.color = BLACK;
            }
        }

        private Node deleteMin(Node node) {
            if (node.left == null) {
                return node.right;
            }

            if (!isRed(node.left) && !isRed(node.left.left)) {
                node = moveRedLeft(node);
            }

            node.left = deleteMin(node.left);
            return balance(node);
        }

        public void deleteMax() {
            if (isEmpty()) {
                return;
            }

            if (!isRed(root.left) && !isRed(root.right)) {
                root.color = RED;
            }

            root = deleteMax(root);

            if (!isEmpty()) {
                root.color = BLACK;
            }
        }

        private Node deleteMax(Node node) {
            if (isRed(node.left)) {
                node = rotateRight(node);
            }

            if (node.right == null) {
                return node.left;
            }

            if (!isRed(node.right) && !isRed(node.right.left)) {
                node = moveRedRight(node);
            }

            node.right = deleteMax(node.right);
            return balance(node);
        }

        public void delete(Key key) {
            if (isEmpty()) {
                return;
            }

            if (!contains(key)) {
                return;
            }

            if (!isRed(root.left) && !isRed(root.right)) {
                root.color = RED;
            }

            root = delete(root, key);

            if (!isEmpty()) {
                root.color = BLACK;
            }
        }

        private Node delete(Node node, Key key) {
            if (node == null) {
                return null;
            }

            if (key.compareTo(node.key) < 0) {
                if (!isRed(node.left) && node.left != null && !isRed(node.left.left)) {
                    node = moveRedLeft(node);
                }

                node.left = delete(node.left, key);
            } else {
                if (isRed(node.left)) {
                    node = rotateRight(node);
                }

                if (key.compareTo(node.key) == 0 && node.right == null) {
                    return node.left;
                }

                if (!isRed(node.right) && node.right != null && !isRed(node.right.left)) {
                    node = moveRedRight(node);
                }

                if (key.compareTo(node.key) == 0) {
                    Node aux = min(node.right);
                    node.key = aux.key;
                    node.value = aux.value;
                    node.right = deleteMin(node.right);
                } else {
                    node.right = delete(node.right, key);
                }
            }

            return balance(node);
        }

        private Node moveRedLeft(Node node) {
            //Assuming that node is red and both node.left and node.left.left are black,
            // make node.left or one of its children red
            flipColors(node);

            if (node.right != null && isRed(node.right.left)) {
                node.right = rotateRight(node.right);
                node = rotateLeft(node);
                flipColors(node);
            }

            return node;
        }

        private Node moveRedRight(Node node) {
            //Assuming that node is red and both node.right and node.right.left are black,
            // make node.right or one of its children red
            flipColors(node);

            if (node.left != null && isRed(node.left.left)) {
                node = rotateRight(node);
                flipColors(node);
            }

            return node;
        }

        private Node balance(Node node) {
            if (node == null) {
                return null;
            }

            if (isRed(node.right) && isRed(node.right.right)) {
                node = rotateLeft(node);
            }

            if (isRed(node.left) && isRed(node.left.left)) {
                node = rotateRight(node);
            }

            if (isRed(node.left) && isRed(node.right)) {
                flipColors(node);
            }

            node.size = size(node.left) + 1 + size(node.right);

            return node;
        }

        public Iterable<Key> keys() {
            return keys(min(), max());
        }

        public Iterable<Key> keys(Key low, Key high) {
            if (low == null)  {
                throw new IllegalArgumentException("First argument to keys() cannot be null");
            }
            if (high == null) {
                throw new IllegalArgumentException("Second argument to keys() cannot be null");
            }

            Queue<Key> queue = new Queue<>();
            keys(root, queue, low, high);
            return queue;
        }

        private void keys(Node node, Queue<Key> queue, Key low, Key high) {
            if (node == null) {
                return;
            }

            int compareLow = low.compareTo(node.key);
            int compareHigh = high.compareTo(node.key);

            if (compareLow < 0) {
                keys(node.left, queue, low, high);
            }

            if (compareLow <= 0 && compareHigh >= 0) {
                queue.enqueue(node.key);
            }

            if (compareHigh > 0) {
                keys(node.right, queue, low, high);
            }
        }

        public int size(Key low, Key high) {
            if (low == null)  {
                throw new IllegalArgumentException("First argument to size() cannot be null");
            }
            if (high == null) {
                throw new IllegalArgumentException("Second argument to size() cannot be null");
            }

            if (low.compareTo(high) > 0) {
                return 0;
            }

            if (contains(high)) {
                return rank(high) - rank(low) + 1;
            } else {
                return rank(high) - rank(low);
            }
        }

        private int internalPathLength() {
            if (root == null) {
                return 0;
            }

            int internalPathLength = 0;

            Queue<Node> queue = new Queue<>();
            root.depth = 0;
            queue.enqueue(root);

            while (!queue.isEmpty()) {
                Node current = queue.dequeue();
                internalPathLength += current.depth;

                if (current.left != null) {
                    current.left.depth = current.depth + 1;
                    queue.enqueue(current.left);
                }
                if (current.right != null) {
                    current.right.depth = current.depth + 1;
                    queue.enqueue(current.right);
                }
            }

            return internalPathLength;
        }

        public int averagePathLength() {
            if (size() == 0) {
                return 0;
            }

            return (internalPathLength() / size()) + 1;
        }

        private boolean isTwoThreeTree() {
            return isTwoThreeTree(root);
        }

        private boolean isTwoThreeTree(Node node) {
            if (node == null) {
                return true;
            }

            if (isRed(node.left) && (isRed(node.left.left) || isRed(node.left.right))) {
                return false;
            }

            if (isRed(node.right) && (isRed(node.right.left) || isRed(node.right.right))) {
                return false;
            }

            return isTwoThreeTree(node.left) && isTwoThreeTree(node.right);
        }

    }

    public static void main(String[] args) {
        Exercise23_23TreesWithoutBalance treesWithoutBalance = new Exercise23_23TreesWithoutBalance();
        treesWithoutBalance.testAPI();
        treesWithoutBalance.doExperiment();
    }

    private void testAPI() {
        // Expected tree
        //            (B)5
        //       (R)1      (B)9
        //    (B)0  (B)2     (R)99
        //  (R)-1
        //(B)-2

        TwoThreeTreeNonBalanced<Integer, Integer> twoThreeTreeNonBalanced = new TwoThreeTreeNonBalanced<>();
        twoThreeTreeNonBalanced.put(5, 5);
        twoThreeTreeNonBalanced.put(1, 1);
        twoThreeTreeNonBalanced.put(9, 9);
        twoThreeTreeNonBalanced.put(2, 2);
        twoThreeTreeNonBalanced.put(0, 0);
        twoThreeTreeNonBalanced.put(99, 99);
        twoThreeTreeNonBalanced.put(-1, -1);
        twoThreeTreeNonBalanced.put(-2, -2);

        StdOut.println("Keys() test");

        for (Integer key : twoThreeTreeNonBalanced.keys()) {
            StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
        }
        StdOut.println("Expected: -2 -1 0 1 2 5 9 99\n");

        // Test min()
        StdOut.println("Min key: " + twoThreeTreeNonBalanced.min() + " Expected: -2");

        // Test max()
        StdOut.println("Max key: " + twoThreeTreeNonBalanced.max() + " Expected: 99");

        // Test floor()
        StdOut.println("Floor of 5: " + twoThreeTreeNonBalanced.floor(5) + " Expected: 5");
        StdOut.println("Floor of 15: " + twoThreeTreeNonBalanced.floor(15) + " Expected: 9");

        // Test ceiling()
        StdOut.println("Ceiling of 5: " + twoThreeTreeNonBalanced.ceiling(5) + " Expected: 5");
        StdOut.println("Ceiling of 15: " + twoThreeTreeNonBalanced.ceiling(15) + " Expected: 99");

        // Test select()
        StdOut.println("Select key of rank 4: " + twoThreeTreeNonBalanced.select(4) + " Expected: 2");

        // Test rank()
        StdOut.println("Rank of key 9: " + twoThreeTreeNonBalanced.rank(9) + " Expected: 6");
        StdOut.println("Rank of key 10: " + twoThreeTreeNonBalanced.rank(10) + " Expected: 7");

        // Test delete()
        StdOut.println("\nDelete key 2");
        twoThreeTreeNonBalanced.delete(2);

        for (Integer key : twoThreeTreeNonBalanced.keys()) {
            StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
        }
        StdOut.println("Is 2-3 three: " + twoThreeTreeNonBalanced.isTwoThreeTree() + " Expected: true");

        // Test deleteMin()
        StdOut.println("\nDelete min (key -2)");
        twoThreeTreeNonBalanced.deleteMin();

        for (Integer key : twoThreeTreeNonBalanced.keys()) {
            StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
        }
        StdOut.println("Is 2-3 three: " + twoThreeTreeNonBalanced.isTwoThreeTree() + " Expected: true");

        // Test deleteMax()
        StdOut.println("\nDelete max (key 99)");
        twoThreeTreeNonBalanced.deleteMax();

        for (Integer key : twoThreeTreeNonBalanced.keys()) {
            StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
        }
        StdOut.println("Is 2-3 three: " + twoThreeTreeNonBalanced.isTwoThreeTree() + " Expected: true");

        // Test keys() with range
        StdOut.println("\nKeys in range [2, 10]");
        for (Integer key : twoThreeTreeNonBalanced.keys(2, 10)) {
            StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
        }

        // Delete all
        StdOut.println("\nDelete all");
        while (twoThreeTreeNonBalanced.size() > 0) {
            for (Integer key : twoThreeTreeNonBalanced.keys()) {
                StdOut.println("Key " + key + ": " + twoThreeTreeNonBalanced.get(key));
            }
            StdOut.println("Is 2-3 three: " + twoThreeTreeNonBalanced.isTwoThreeTree() + " Expected: true");

            twoThreeTreeNonBalanced.delete(twoThreeTreeNonBalanced.select(twoThreeTreeNonBalanced.size() - 1));
            StdOut.println();
        }
    }

    private void doExperiment() {
        int redBlackTreeSize = 100;

        for (int experiment = 0; experiment < 6; experiment++) {
            TwoThreeTreeNonBalanced<Integer, Integer> twoThreeTreeNonBalanced = new TwoThreeTreeNonBalanced<>();

            for (int i = 0; i < redBlackTreeSize; i++) {
                int randomKey = StdRandom.uniform(Integer.MAX_VALUE);
                twoThreeTreeNonBalanced.put(randomKey, randomKey);
            }

            StdOut.println("Average path length for " + redBlackTreeSize + " random insertions: "
                    + twoThreeTreeNonBalanced.averagePathLength());
            redBlackTreeSize *= 10;
        }
    }
}

Additional notes/results:
3.3.23 - 2-3 trees without balance restriction

Average path length for 100 random insertions: 7
Average path length for 1000 random insertions: 12
Average path length for 10000 random insertions: 17
Average path length for 100000 random insertions: 20
Average path length for 1000000 random insertions: 26
Average path length for 10000000 random insertions: 30

Hypothesis: In a non-balanced 2-3 tree the average path length becomes the same as the average path length for a standard binary search tree: ~1.44 lg N after N random insertions.


Question: Allow right-leaning red links. Develop a modified version of your solution to Exercise 3.3.25 that allows right-leaning red links in the tree.

Answer:
package chapter3.section3;

import edu.princeton.cs.algs4.StdOut;

/**
 * Created by Rene Argento on 28/06/17.
 */
public class Exercise27_AllowRightLeaningRedLinks {

    private class RedBlackTopDown234RightLeaningBST<Key extends Comparable<Key>, Value> extends Exercise25_TopDown234Trees.RedBlackTopDown234BST<Key, Value> {
        public void put(Key key, Value value) {
            if (key == null) {
                return;
            }

            if (value == null) {
                delete(key);
                return;
            }

            root = put(root, key, value);
            root.color = BLACK;
        }

        private Node put(Node node, Key key, Value value) {
            if (node == null) {
                return new Node(key, value, 1, RED);
            }

            if (isRed(node.left) && isRed(node.right)) {
                flipColors(node);
            }

            int compare = key.compareTo(node.key);

            if (compare < 0) {
                node.left = put(node.left, key, value);
            } else if (compare > 0) {
                node.right = put(node.right, key, value);
            } else {
                node.value = value;
            }

            if (isRed(node.right) && isRed(node.right.left)) {
                node.right = rotateRight(node.right);
            }
            if (isRed(node.right) && isRed(node.right.right)) {
                node = rotateLeft(node);
            }
            if (isRed(node.left) && isRed(node.left.right)) {
                node.left = rotateLeft(node.left);
            }
            if (isRed(node.left) && isRed(node.left.left)) {
                node = rotateRight(node);
            }

            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public void deleteMax() {
            if (isEmpty()) {
                return;
            }

            if (!isRed(root.left) && !isRed(root.right)) {
                root.color = RED;
            }

            root = deleteMax(root);

            if (!isEmpty()) {
                root.color = BLACK;
            }
        }

        private Node deleteMax(Node node) {
            if (isRed(node.left) && !isRed(node.right)) {
                node = rotateRight(node);
            }

            if (node.right == null) {
                return null;
            }

            if (!isRed(node.right) && node.right != null && !isRed(node.right.left) && !isRed(node.right.right)) {
                node = moveRedRight(node);
            }

            node.right = deleteMax(node.right);
            return balance(node);
        }

        public void delete(Key key) {
            if (isEmpty()) {
                return;
            }

            if (!contains(key)) {
                return;
            }

            if (!isRed(root.left) && !isRed(root.right)) {
                root.color = RED;
            }

            root = delete(root, key);

            if (!isEmpty()) {
                root.color = BLACK;
            }
        }

        private Node delete(Node node, Key key) {
            if (node == null) {
                return null;
            }

            if (key.compareTo(node.key) < 0) {
                if (!isRed(node.left) && node.left != null && !isRed(node.left.left)) {
                    node = moveRedLeft(node);
                }

                node.left = delete(node.left, key);
            } else {
                if (isRed(node.left) && !isRed(node.right)) {
                    node = rotateRight(node);
                }

                if (key.compareTo(node.key) == 0 && node.right == null) {
                    return null;
                }

                if (!isRed(node.right) && node.right != null && !isRed(node.right.left) && !isRed(node.right.right)) {
                    node = moveRedRight(node);
                }

                if (key.compareTo(node.key) == 0) {
                    Node aux = min(node.right);
                    node.key = aux.key;
                    node.value = aux.value;
                    node.right = deleteMin(node.right);
                } else {
                    node.right = delete(node.right, key);
                }
            }

            return balance(node);
        }

        private Node moveRedLeft(Node node) {
            //Assuming that node is red and node.left and node.left.left and node.left.right are black,
            // make node.left or one of its children red
            flipColors(node);

            if (node.right != null && isRed(node.right.left)) {
                node.right = rotateRight(node.right);
                node = rotateLeft(node);
                flipColors(node);
            } else if (node.right != null && isRed(node.right.right)) {
                node.right = rotateLeft(node.right);
                node = rotateLeft(node);
                flipColors(node);
            }

            return node;
        }

        private Node moveRedRight(Node node) {
            // Assuming that node is red and node.right and node.right.left and node.right.right are black,
            // make node.right or one of its children red
            flipColors(node);

            if (node.left != null && isRed(node.left.left)) {
                node = rotateRight(node);
                flipColors(node);
            } else if (node.left != null && isRed(node.left.right)) {
                node.left = rotateLeft(node.left);
                node = rotateRight(node);
                flipColors(node);
            }

            return node;
        }

        private Node balance(Node node) {
            if (node == null) {
                return null;
            }

            if (isRed(node.right) && isRed(node.right.left)) {
                node.right = rotateRight(node.right);
            }
            if (isRed(node.right) && isRed(node.right.right)) {
                node = rotateLeft(node);
            }
            if (isRed(node.left) && isRed(node.left.right)) {
                node.left = rotateLeft(node.left);
            }
            if (isRed(node.left) && isRed(node.left.left)) {
                node = rotateRight(node);
            }

            if (isRed(node.left) && isRed(node.right)) {
                flipColors(node);
            }

            node.size = size(node.left) + 1 + size(node.right);

            return node;
        }

        public boolean isValid234RightLeaningTree() {
            return isValid234RightLeaningTree(root);
        }

        private boolean isValid234RightLeaningTree(Node node) {
            if (node == null) {
                return true;
            }

            if (isRed(node.left) && isRed(node.left.left)) {
                return false;
            }
            if (isRed(node.left) && isRed(node.left.right)) {
                return false;
            }
            if (isRed(node.right) && isRed(node.right.right)) {
                return false;
            }
            if (isRed(node.right) && isRed(node.right.left)) {
                return false;
            }

            return isValid234RightLeaningTree(node.left) && isValid234RightLeaningTree(node.right);
        }
    }

    public static void main(String[] args) {
        // Expected 2-3-4 tree
        //
        //                 (B)1
        //         (R)-1          (R)5
        //    (B)-2   (B)0    (B)2    (B)9
        // (R)-5                (R)3    (R)99
        //

        Exercise27_AllowRightLeaningRedLinks allowRightLeaningRedLinks = new Exercise27_AllowRightLeaningRedLinks();
        RedBlackTopDown234RightLeaningBST<Integer, Integer> redBlackTopDown234RightLeaningBST =
                allowRightLeaningRedLinks.new RedBlackTopDown234RightLeaningBST<>();
        redBlackTopDown234RightLeaningBST.put(5, 5);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(1, 1);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(9, 9);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(2, 2);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(0, 0);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(99, 99);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(-1, -1);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(-2, -2);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(3, 3);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        redBlackTopDown234RightLeaningBST.put(-5, -5);
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true\n");

        StdOut.println("Size consistent: " + redBlackTopDown234RightLeaningBST.isSubtreeCountConsistent() + " Expected: true\n");

        StdOut.println("Keys() test");

        for (Integer key : redBlackTopDown234RightLeaningBST.keys()) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }
        StdOut.println("Expected: -5 -2 -1 0 1 2 3 5 9 99\n");

        // Test min()
        StdOut.println("Min key: " + redBlackTopDown234RightLeaningBST.min() + " Expected: -5");

        // Test max()
        StdOut.println("Max key: " + redBlackTopDown234RightLeaningBST.max() + " Expected: 99");

        // Test floor()
        StdOut.println("Floor of 5: " + redBlackTopDown234RightLeaningBST.floor(5) + " Expected: 5");
        StdOut.println("Floor of 15: " + redBlackTopDown234RightLeaningBST.floor(15) + " Expected: 9");

        // Test ceiling()
        StdOut.println("Ceiling of 5: " + redBlackTopDown234RightLeaningBST.ceiling(5) + " Expected: 5");
        StdOut.println("Ceiling of 15: " + redBlackTopDown234RightLeaningBST.ceiling(15) + " Expected: 99");

        // Test select()
        StdOut.println("Select key of rank 4: " + redBlackTopDown234RightLeaningBST.select(4) + " Expected: 1");

        // Test rank()
        StdOut.println("Rank of key 9: " + redBlackTopDown234RightLeaningBST.rank(9) + " Expected: 8");
        StdOut.println("Rank of key 10: " + redBlackTopDown234RightLeaningBST.rank(10) + " Expected: 9");

        // Test delete()
        StdOut.println("\nDelete key 2");
        redBlackTopDown234RightLeaningBST.delete(2);

        for (Integer key : redBlackTopDown234RightLeaningBST.keys()) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        StdOut.println("Size consistent: " + redBlackTopDown234RightLeaningBST.isSubtreeCountConsistent() + " Expected: true");

        // Test deleteMin()
        StdOut.println("\nDelete min (key -5)");
        redBlackTopDown234RightLeaningBST.deleteMin();

        for (Integer key : redBlackTopDown234RightLeaningBST.keys()) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        StdOut.println("Size consistent: " + redBlackTopDown234RightLeaningBST.isSubtreeCountConsistent() + " Expected: true");

        // Test deleteMax()
        StdOut.println("\nDelete max (key 99)");
        redBlackTopDown234RightLeaningBST.deleteMax();

        for (Integer key : redBlackTopDown234RightLeaningBST.keys()) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }
        StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
        StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
        StdOut.println("Size consistent: " + redBlackTopDown234RightLeaningBST.isSubtreeCountConsistent() + " Expected: true");

        // Test keys() with range
        StdOut.println("\nKeys in range [2, 10]");
        for (Integer key : redBlackTopDown234RightLeaningBST.keys(2, 10)) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }

        StdOut.println("\nKeys in range [-4, -1]");
        for (Integer key : redBlackTopDown234RightLeaningBST.keys(-4, -1)) {
            StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
        }

        // Delete all
        StdOut.println("\nDelete all");
        while (redBlackTopDown234RightLeaningBST.size() > 0) {
            for (Integer key : redBlackTopDown234RightLeaningBST.keys()) {
                StdOut.println("Key " + key + ": " + redBlackTopDown234RightLeaningBST.get(key));
            }

            // redBlackIterative234BST.delete(redBlackIterative234BST.select(0));
            redBlackTopDown234RightLeaningBST.delete(redBlackTopDown234RightLeaningBST.select(redBlackTopDown234RightLeaningBST.size() - 1));
            StdOut.println("Is valid 2-3-4 tree: " + redBlackTopDown234RightLeaningBST.isValid234RightLeaningTree() + " Expected: true");
            StdOut.println("Is BST: " + redBlackTopDown234RightLeaningBST.isBST() + " Expected: true");
            StdOut.println("Size consistent: " + redBlackTopDown234RightLeaningBST.isSubtreeCountConsistent() + " Expected: true");

            StdOut.println();
        }
    }

}


Question: Tree drawing. Add a method draw() to RedBlackBST that draws red-black BST figures in the style of the text (see Exercise 3.2.38).

Answer:
package chapter3.section3;

import edu.princeton.cs.algs4.StdDraw;

/**
 * Created by Rene Argento on 18/06/17.
 */
public class Exercise31_TreeDrawing {

    public class RedBlackBSTDrawable<Key extends Comparable<Key>, Value> extends RedBlackBST<Key, Value> {

        private class Node {
            Key key;
            Value value;
            Node left, right;

            boolean color;
            int size;

            private double xCoordinate, yCoordinate;

            Node(Key key, Value value, int size, boolean color) {
                this.key = key;
                this.value = value;

                this.size = size;
                this.color = color;
            }
        }

        private Node root;
        private int treeLevel;

        public int size() {
            return size(root);
        }

        protected int size(Node node) {
            if (node == null) {
                return 0;
            }

            return node.size;
        }

        private boolean isRed(Node node) {
            if (node == null) {
                return false;
            }

            return node.color == RED;
        }

        private Node rotateLeft(Node node) {
            if (node == null || node.right == null) {
                return node;
            }

            Node newRoot = node.right;

            node.right = newRoot.left;
            newRoot.left = node;

            newRoot.color = node.color;
            node.color = RED;

            newRoot.size = node.size;
            node.size = size(node.left) + 1 + size(node.right);

            return newRoot;
        }

        private Node rotateRight(Node node) {
            if (node == null || node.left == null) {
                return node;
            }

            Node newRoot = node.left;

            node.left = newRoot.right;
            newRoot.right = node;

            newRoot.color = node.color;
            node.color = RED;

            newRoot.size = node.size;
            node.size = size(node.left) + 1 + size(node.right);

            return newRoot;
        }

        private void flipColors(Node node) {
            if (node == null || node.left == null || node.right == null) {
                return;
            }

            node.color = RED;
            node.left.color = BLACK;
            node.right.color = BLACK;
        }

        public void put(Key key, Value value) {
            if (key == null) {
                return;
            }

            root = put(root, key, value);
            root.color = BLACK;
        }

        private Node put(Node node, Key key, Value value) {
            if (node == null) {
                return new Node(key, value, 1, RED);
            }

            int compare = key.compareTo(node.key);

            if (compare < 0) {
                node.left = put(node.left, key, value);
            } else if (compare > 0) {
                node.right = put(node.right, key, value);
            } else {
                node.value = value;
            }

            if (isRed(node.right) && !isRed(node.left)) {
                node = rotateLeft(node);
            }
            if (isRed(node.left) && isRed(node.left.left)) {
                node = rotateRight(node);
            }
            if (isRed(node.left) && isRed(node.right)) {
                flipColors(node);
            }

            node.size = size(node.left) + 1 + size(node.right);
            return node;
        }

        public void draw() {
            treeLevel = 0;
            setCoordinates(root, 0.9);

            StdDraw.setPenColor(StdDraw.BLACK);
            drawLines(root);
            drawNodes(root);
        }

        private void setCoordinates(Node node, double distance) {
            if (node == null) {
                return;
            }

            setCoordinates(node.left, distance - 0.05);
            node.xCoordinate = (0.5 + treeLevel++) / size();
            node.yCoordinate = distance - 0.05;
            setCoordinates(node.right, distance - 0.05);
        }

        private void drawLines(Node node) {
            if (node == null) {
                return;
            }

            drawLines(node.left);

            if (node.left != null) {
                if (node.left.color == RED) {
                    setPenToWriteRedLine();
                }

                StdDraw.line(node.xCoordinate, node.yCoordinate, node.left.xCoordinate, node.left.yCoordinate);
                resetPen();
            }
            if (node.right != null) {
                if (node.right.color == RED) {
                    setPenToWriteRedLine();
                }

                StdDraw.line(node.xCoordinate, node.yCoordinate, node.right.xCoordinate, node.right.yCoordinate);
                resetPen();
            }

            drawLines(node.right);
        }

        private void setPenToWriteRedLine() {
            StdDraw.setPenColor(StdDraw.RED);
            StdDraw.setPenRadius(0.007);
        }

        private void resetPen() {
            StdDraw.setPenColor(StdDraw.BLACK);
            StdDraw.setPenRadius(0.0025);
        }

        private void drawNodes(Node node) {
            if (node == null) {
                return;
            }

            double nodeRadius = 0.032;

            drawNodes(node.left);

            StdDraw.setPenColor(StdDraw.WHITE);
            //Clear the node circle area
            StdDraw.filledCircle(node.xCoordinate, node.yCoordinate, nodeRadius);

            StdDraw.setPenColor(StdDraw.BLACK);
            StdDraw.circle(node.xCoordinate, node.yCoordinate, nodeRadius);
            StdDraw.text(node.xCoordinate, node.yCoordinate, String.valueOf(node.key));

            drawNodes(node.right);
        }
    }

    public static void main(String[] args) {
        StdDraw.setPenRadius(0.0025);
        Exercise31_TreeDrawing treeDrawing = new Exercise31_TreeDrawing();

        RedBlackBSTDrawable<Integer, String> redBlackBSTDrawable = treeDrawing.new RedBlackBSTDrawable();

        /**
         *            (B)6
         *    (B)2            (B)20
         * (B)1 (B)4     (R)12     (B)25
         *            (B)10 (B)15
         */

        redBlackBSTDrawable.put(10, "Value 10");
        redBlackBSTDrawable.put(4, "Value 4");
        redBlackBSTDrawable.put(6, "Value 6");
        redBlackBSTDrawable.put(1, "Value 1");
        redBlackBSTDrawable.put(2, "Value 2");
        redBlackBSTDrawable.put(15, "Value 15");
        redBlackBSTDrawable.put(12, "Value 12");
        redBlackBSTDrawable.put(20, "Value 20");
        redBlackBSTDrawable.put(25, "Value 25");

        StdDraw.clear(StdDraw.WHITE);
        redBlackBSTDrawable.draw();
    }

}