William Jacobs 148e9247d3 Clarified main documentation
This changes README.md and the comment for RedBlackNode to more clearly explain what the project is all about. It emphasizes the fact that RedBlackNode provides public access to the tree's structure. It changes the usage example in README.md from a short RedBlackNode subclass highlighting how easy augmentation is to a medium-length pair of tree and node classes that show how to use insertion, removal, and augmentation.

This change also makes minor improvements to comments for RedBlackNode methods.
2019-03-04 17:01:46 -05:00
2016-05-23 13:51:21 -07:00
2016-05-23 13:51:21 -07:00
2016-05-26 12:22:32 -07:00
2016-05-23 13:51:21 -07:00
2016-05-23 13:43:59 -07:00
2019-03-04 17:01:46 -05:00
2019-03-04 17:01:46 -05:00

Description

RedBlackNode is a Java implementation of red-black trees. Compared to a class like Java's TreeMap, RedBlackNode is a low-level data structure. The internals of each node are exposed as public fields, allowing clients to directly observe and manipulate the structure of the tree. This gives clients flexibility, although it also enables them to violate the red-black or BST properties. The RedBlackNode class provides methods for performing various standard operations, such as insertion and removal.

Unlike most implementations of binary search trees, RedBlackNode supports arbitrary augmentation. By subclassing RedBlackNode, clients can add arbitrary data and augmentation information to each node.

Features

  • Supports min, max, root, predecessor, successor, insert, remove, rotate, split, concatenate, create balanced tree, LCA, and compare operations. The running time of each operation has optimal big O bounds.
  • Supports arbitrary augmentation by overriding augment(). Examples of augmentation are the number of non-leaf nodes in a subtree and the sum of the values in a subtree. All RedBlackNode methods (such as insert and remove()) call augment() as necessary to correctly maintain the augmentation information, unless otherwise indicated in their comments.
  • The parent and child links and the color are public fields. This gives clients flexibility, although it also enables them to violate the red-black or BST properties.
  • "Assert is valid" methods allow clients to check for errors in the structure or contents of a red-black tree. This is useful for debugging.
  • As a bonus (a proof of concept and a test case), this includes the TreeList class, a List implementation backed by a red-black tree augmented by subtree size.
  • Tested in Java 6.0, 7.0, and 8.0.

Limitations

  • The values of the tree must be stored in the non-leaf nodes. RedBlackNode does not support use cases where the values must be stored in the leaf nodes.
  • Augmentations that depend on information stored in a node's ancestors are not (easily) supported. For example, augmenting each node with the number of nodes in the left subtree is not (easily and efficiently) supported, because in order to perform a right rotation, we would need to use the parent's augmentation information. However, RedBlackNode supports augmenting each node with the number of nodes in the subtree, which is basically equivalent.
  • The running time of each operation has optimal big O bounds. However, beyond this, no special effort has been made to optimize performance.

Example usage

class Node<T> extends RedBlackNode<Node<T>> {
    /** The value we are storing in the node. */
    public final T value;

    /** The number of nodes in this subtree. */
    public int size;

    public Node(T value) {
        this.value = value;
    }

    @Override
    public boolean augment() {
        int newSize = left.size + right.size + 1;
        if (newSize == size) {
            return false;
        } else {
            size = newSize;
            return true;
        }
    }
}
/** Stores a set of distinct values. */
public class Tree<T extends Comparable<? super T>> {
    /** The dummy leaf node. */
    private final Node<T> leaf = new Node<T>(null);

    private Node<T> root = leaf;

    public void add(T value) {
        // A comparator telling "insert" where to put the new node
        Comparator<Node<T>> comparator = new Comparator<Node<T>>() {
            public int compare(Node<T> node1, Node<T> node2) {
                return node1.value.compareTo(node2.value);
            }
        };

        Node<T> newNode = new Node<T>(value);
        root = root.insert(newNode, false, comparator);
    }

    /** Returns the node containing the specified value, if any. */
    private Node<T> find(T value) {
        Node<T> node = root;
        while (!node.isLeaf()) {
            int c = value.compareTo(node.value);
            if (c == 0) {
                return node;
            } else if (c < 0) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        return null;
    }

    public boolean contains(T value) {
        return find(value) != null;
    }

    public void remove(T value) {
        Node<T> node = find(value);
        if (node != null) {
            root = node.remove();
        }
    }

    /** Returns the (rank + 1)th node in the subtree rooted at "node". */
    private Node<T> getNodeWithRank(Node<T> node, int rank) {
        if (rank < 0 || rank >= node.size) {
            throw new IndexOutOfBoundsException();
        }
        if (rank == node.left.size) {
            return node;
        } else if (rank < node.left.size) {
            return getNodeWithRank(node.left, rank);
        } else {
            return getNodeWithRank(node.right, rank - node.left.size - 1);
        }
    }

    /** Returns the (rank + 1)th-smallest value in the tree. */
    public T getItemWithRank(int rank) {
        return getNodeWithRank(root, rank).value;
    }
}

Documentation

For more detailed instructions, check the source code to see the full API and Javadoc documentation.

Languages
Java 100%