From 148e9247d3370ef7a0d630d7300a2125e7261fcd Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Mon, 4 Mar 2019 17:01:46 -0500 Subject: [PATCH] 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. --- README.md | 139 ++++++++++++++---- RedBlackNode.jar | Bin 43628 -> 44381 bytes .../btrekkie/red_black_node/RedBlackNode.java | 97 +++++++----- 3 files changed, 168 insertions(+), 68 deletions(-) diff --git a/README.md b/README.md index 2e27cdde9..ecb3225dc 100644 --- a/README.md +++ b/README.md @@ -1,61 +1,140 @@ -# RedBlackNode -`RedBlackNode` is a Java implementation of red-black trees. By subclassing -`RedBlackNode`, clients can add arbitrary data and augmentation information to -each node. (self-balancing binary search tree, self-balancing BST, augment, -augmented) +# 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 + 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 +* 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. -* The parent and child links and the color are public fields. This gives - clients flexibility. However, it is possible for a client to violate the - red-black or BST properties. + 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. + 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 and 7.0. It might also work in Java 5.0. +* 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` +* 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 + (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 +* The running time of each operation has optimal big O bounds. However, beyond this, no special effort has been made to optimize performance. -# Example -
-/** Red-black tree augmented by the sum of the values in the subtree. */
-public class SumNode extends RedBlackNode<SumNode> {
-    public int value;
-    public int sum;
+# Example usage
+```java
+class Node extends RedBlackNode> {
+    /** The value we are storing in the node. */
+    public final T value;
 
-    public SumNode(int value) {
+    /** The number of nodes in this subtree. */
+    public int size;
+
+    public Node(T value) {
         this.value = value;
     }
 
     @Override
     public boolean augment() {
-        int newSum = value + left.sum + right.sum;
-        if (newSum == sum) {
+        int newSize = left.size + right.size + 1;
+        if (newSize == size) {
             return false;
         } else {
-            sum = newSum;
+            size = newSize;
             return true;
         }
     }
 }
-
+``` + +```java +/** Stores a set of distinct values. */ +public class Tree> { + /** The dummy leaf node. */ + private final Node leaf = new Node(null); + + private Node root = leaf; + + public void add(T value) { + // A comparator telling "insert" where to put the new node + Comparator> comparator = new Comparator>() { + public int compare(Node node1, Node node2) { + return node1.value.compareTo(node2.value); + } + }; + + Node newNode = new Node(value); + root = root.insert(newNode, false, comparator); + } + + /** Returns the node containing the specified value, if any. */ + private Node find(T value) { + Node 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 node = find(value); + if (node != null) { + root = node.remove(); + } + } + + /** Returns the (rank + 1)th node in the subtree rooted at "node". */ + private Node getNodeWithRank(Node 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 diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 234edde7aba36f98c25c31731a07f01ef59654cd..3a126d57a5ebfdade57e1623ee197a549ef15103 100644 GIT binary patch delta 12868 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 Xw{1mSK?FgX1rn$rf_cdLC-naT^I&c0 diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 671c35b6b..e045b380d 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -8,22 +8,33 @@ import java.util.Iterator; import java.util.Set; /** - * A node in a red-black tree ( https://en.wikipedia.org/wiki/Red%E2%80%93black_tree ). The RedBlackNode class provides - * methods for performing various standard operations. + * A node in a red-black tree ( https://en.wikipedia.org/wiki/Red%E2%80%93black_tree ). Compared to a class like Java's + * TreeMap, RedBlackNode is a low-level data structure. The internals of a 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. * - * Subclasses may add arbitrary information to the node. For example, if we were to use a RedBlackNode subclass to - * implement a sorted set, the subclass should have a field storing an element in the set. Subclasses can augment the - * tree with arbitrary information by overriding augment(). + * 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. For example, if we were to + * use a RedBlackNode subclass to implement a sorted set, the subclass would have a field storing an element in the set. + * If we wanted to keep track of the number of non-leaf nodes in each subtree, we would store this as a "size" field and + * override augment() to update this field. All RedBlackNode methods (such as "insert" and remove()) call augment() as + * necessary to correctly maintain the augmentation information, unless otherwise indicated. * - * The values of the tree are stored in the non-leaf nodes. RedBlackNode does not support use cases where values must - * be stored in the leaf nodes. It is recommended that all of the leaf nodes in a given tree be the same (black) - * RedBlackNode instance, to save space. The root of an empty tree is a leaf node, as opposed to null. + * The values of the tree are stored in the non-leaf nodes. RedBlackNode does not support use cases where values must be + * stored in the leaf nodes. It is recommended that all of the leaf nodes in a given tree be the same (black) + * RedBlackNode instance, to save space. The root of an empty tree is a leaf node, as opposed to null. * - * The internals of the node are exposed publicly, so clients can access or alter a node arbitrarily. This gives - * clients flexibility. It is possible for a client to violate the red-black or BST properties. + * For reference, a red-black tree is a binary search tree satisfying the following properties: * - * @param The type of node in the tree. For example, we might have "class FooNode extends - * RedBlackNode>". + * - Every node is colored red or black. + * - The leaf nodes, which are dummy nodes that do not store any values, are colored black. + * - The root is black. + * - Both children of each red node are black. + * - Every path from the root to a leaf contains the same number of black nodes. + * + * @param The type of node in the tree. For example, we might have + * "class FooNode extends RedBlackNode>". * @author Bill Jacobs */ public abstract class RedBlackNode> implements Comparable { @@ -64,7 +75,7 @@ public abstract class RedBlackNode> implements Compara * children, this is equivalent to whether the augmentation information changed as a result of this call to * augment(). For example, in the case of subtree size, this returns whether the value of the size field prior to * calling augment() differed from the size field of the left child plus the size field of the right child plus one. - * False positives are permitted. The return value is unspecified if we have not called augment() on this subtree + * False positives are permitted. The return value is unspecified if we have not called augment() on this node * before. * * This method may assume that this is not a leaf node. It may not assume that the augmentation information stored @@ -172,8 +183,9 @@ public abstract class RedBlackNode> implements Compara } /** - * Performs a left rotation about this node. This method assumes that !isLeaf() && !right.isLeaf(). It calls - * augment() on this node and on its resulting parent. + * Performs a left rotation about this node. This method assumes that !isLeaf() && !right.isLeaf(). It calls + * augment() on this node and on its resulting parent. However, it does not call augment() on any of the resulting + * parent's ancestors, because that is normally the responsibility of the caller. * @return The return value from calling augment() on the resulting parent. */ public boolean rotateLeft() { @@ -202,8 +214,9 @@ public abstract class RedBlackNode> implements Compara } /** - * Performs a right rotation about this node. This method assumes that !isLeaf() && !left.isLeaf(). It calls - * augment() on this node and on its resulting parent. + * Performs a right rotation about this node. This method assumes that !isLeaf() && !left.isLeaf(). It calls + * augment() on this node and on its resulting parent. However, it does not call augment() on any of the resulting + * parent's ancestors, because that is normally the responsibility of the caller. * @return The return value from calling augment() on the resulting parent. */ public boolean rotateRight() { @@ -349,7 +362,7 @@ public abstract class RedBlackNode> implements Compara } /** - * Inserts the specified node into the tree rooted at this node. Assumes this is the root. We treat newNode as a + * Inserts the specified node into the tree rooted at this node. Assumes this is the root. We treat newNode as a * solitary node that does not belong to any tree, and we ignore its initial "parent", "left", "right", and isRed * fields. * @@ -357,10 +370,11 @@ public abstract class RedBlackNode> implements Compara * should manually add the node to the appropriate location, color it red, and call fixInsertion(). * * @param newNode The node to insert. - * @param allowDuplicates Whether to insert newNode if there is an equal node in the tree. To check whether we + * @param allowDuplicates Whether to insert newNode if there is an equal node in the tree. To check whether we * inserted newNode, check whether newNode.parent is null and the return value differs from newNode. - * @param comparator A comparator indicating where to put the node. If this is null, we use the nodes' natural - * order, as in N.compareTo. + * @param comparator A comparator indicating where to put the node. If this is null, we use the nodes' natural + * order, as in N.compareTo. If you are passing null, then you must override the compareTo method, because the + * default implementation requires the nodes to already be in the same tree. * @return The root of the resulting tree. */ public N insert(N newNode, boolean allowDuplicates, Comparator comparator) { @@ -705,9 +719,10 @@ public abstract class RedBlackNode> implements Compara } /** - * Returns the root of a perfectly height-balanced tree containing the specified nodes, in iteration order. This - * method is responsible for setting the "left", "right", "parent", and isRed fields of the nodes, and calling - * augment() as appropriate. It ignores the initial values of the "left", "right", "parent", and isRed fields. + * Returns the root of a perfectly height-balanced tree containing the specified nodes, in iteration order. This + * method is responsible for setting the "left", "right", "parent", and isRed fields of the nodes (excluding + * "leaf"), and calling augment() as appropriate. It ignores the initial values of the "left", "right", "parent", + * and isRed fields. * @param nodes The nodes. * @param leaf The leaf node. * @return The root of the tree. @@ -875,7 +890,7 @@ public abstract class RedBlackNode> implements Compara // firstPivot: The node where we last went right, i.e. the next node to use as a pivot when concatenating with // the pre-split tree. // advanceFirst: Whether to set "first" to be its next black descendant at the end of the loop. - // last, lastParent, lastPivot, advanceFirst: Analogous to "first", firstParent, firstPivot, and advanceFirst, + // last, lastParent, lastPivot, advanceLast: Analogous to "first", firstParent, firstPivot, and advanceFirst, // but for the post-split tree. if (parent != null) { throw new IllegalArgumentException("This is not the root of a tree"); @@ -1063,8 +1078,12 @@ public abstract class RedBlackNode> implements Compara /** * Returns the lowest common ancestor of this node and "other" - the node that is an ancestor of both and is not the - * parent of a node that is an ancestor of both. Assumes that this is in the same tree as "other". Assumes that - * neither "this" nor "other" is a leaf node. This method may return "this" or "other". + * parent of a node that is an ancestor of both. Assumes that this is in the same tree as "other". Assumes that + * neither "this" nor "other" is a leaf node. This method may return "this" or "other". + * + * Note that while it is possible to compute the lowest common ancestor in O(P) time, where P is the length of the + * path from this node to "other", the "lca" method is not guaranteed to take O(P) time. If your application + * requires this, then you should write your own lowest common ancestor method. */ public N lca(N other) { if (isLeaf() || other.isLeaf()) { @@ -1108,13 +1127,16 @@ public abstract class RedBlackNode> implements Compara } /** - * Returns an integer comparing the position of this node in the tree that contains it with that of "other". - * Returns a negative number if this is earlier, a positive number if this is later, and 0 if this is at the same - * position. Assumes that this is in the same tree as "other". Assumes that neither "this" nor "other" is a leaf - * node. + * Returns an integer comparing the position of this node in the tree that contains it with that of "other". Returns + * a negative number if this is earlier, a positive number if this is later, and 0 if this is at the same position. + * Assumes that this is in the same tree as "other". Assumes that neither "this" nor "other" is a leaf node. * - * The base class's implementation takes O(log N) time. If a RedBlackNode subclass stores a value used to order the + * The base class's implementation takes O(log N) time. If a RedBlackNode subclass stores a value used to order the * nodes, then it could override compareTo to compare the nodes' values, which would take O(1) time. + * + * Note that while it is possible to compare the positions of two nodes in O(P) time, where P is the length of the + * path from this node to "other", the default implementation of compareTo is not guaranteed to take O(P) time. If + * your application requires this, then you should write your own comparison method. */ @Override public int compareTo(N other) { @@ -1198,11 +1220,11 @@ public abstract class RedBlackNode> implements Compara } /** - * Throws a RuntimeException if this is a repeated node other than a leaf node or the subtree rooted at this node - * does not satisfy the red-black properties, excluding the requirement that the root be black. + * Throws a RuntimeException if the subtree rooted at this node does not satisfy the red-black properties, excluding + * the requirement that the root be black, or it contains a repeated node other than a leaf node. * @param blackHeight The required number of black nodes in each path from this to a leaf node, including this and * the leaf node. - * @param visited The nodes we have reached thus far, other than leaf nodes. This method adds the non-leaf nodes in + * @param visited The nodes we have reached thus far, other than leaf nodes. This method adds the non-leaf nodes in * the subtree rooted at this node to "visited". */ private void assertSubtreeIsValidRedBlack(int blackHeight, Set> visited) { @@ -1255,9 +1277,8 @@ public abstract class RedBlackNode> implements Compara /** * Throws a RuntimeException if the subtree rooted at this node is not a valid red-black tree, e.g. if a red node - * has a red child or it contains a non-leaf node "node" for which node.left.parent != node. (If parent != null, - * it's okay if isRed is true.) This method is useful for debugging. See also - * assertSubtreeIsValid(). + * has a red child or it contains a non-leaf node "node" for which node.left.parent != node. (If parent != null, + * it's okay if isRed is true.) This method is useful for debugging. See also assertSubtreeIsValid(). */ public void assertSubtreeIsValidRedBlack() { if (isLeaf()) {