From d3e6f9ae02fbfd397bdc72f88adf92af69855b8b Mon Sep 17 00:00:00 2001 From: btrekkie Date: Mon, 23 May 2016 13:43:59 -0700 Subject: [PATCH 01/24] Initial commit --- .gitignore | 12 ++++++++++++ LICENSE | 21 +++++++++++++++++++++ README.md | 2 ++ 3 files changed, 35 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..32858aad3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..b5b29e44d --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 btrekkie + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 000000000..bc2c15b8e --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# 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) From 389f8ea4e2a17583650a8515fede34b558127d33 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Mon, 23 May 2016 13:51:21 -0700 Subject: [PATCH 02/24] Initial commit This adds the initial contents of the repository. --- .classpath | 7 + .project | 17 + .settings/org.eclipse.jdt.core.prefs | 11 + .settings/org.eclipse.jdt.ui.prefs | 60 + README.md | 60 +- .../ArbitraryOrderCollection.java | 40 + .../ArbitraryOrderNode.java | 8 + .../ArbitraryOrderValue.java | 19 + .../test/ArbitraryOrderCollectionTest.java | 72 + .../btrekkie/interval_tree/IntervalTree.java | 60 + .../interval_tree/IntervalTreeInterval.java | 28 + .../interval_tree/IntervalTreeNode.java | 68 + .../interval_tree/test/IntervalTreeTest.java | 48 + .../btrekkie/red_black_node/RedBlackNode.java | 1224 +++++++++++++++++ .../btrekkie/red_black_node/Reference.java | 28 + .../red_black_node/test/RedBlackNodeTest.java | 171 +++ .../red_black_node/test/TestRedBlackNode.java | 36 + .../github/btrekkie/tree_list/TreeList.java | 461 +++++++ .../btrekkie/tree_list/TreeListNode.java | 40 + .../btrekkie/tree_list/test/TreeListTest.java | 551 ++++++++ 20 files changed, 3008 insertions(+), 1 deletion(-) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.jdt.ui.prefs create mode 100644 src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java create mode 100644 src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java create mode 100644 src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java create mode 100644 src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java create mode 100644 src/com/github/btrekkie/interval_tree/IntervalTree.java create mode 100644 src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java create mode 100644 src/com/github/btrekkie/interval_tree/IntervalTreeNode.java create mode 100644 src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java create mode 100644 src/com/github/btrekkie/red_black_node/RedBlackNode.java create mode 100644 src/com/github/btrekkie/red_black_node/Reference.java create mode 100644 src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java create mode 100644 src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java create mode 100644 src/com/github/btrekkie/tree_list/TreeList.java create mode 100644 src/com/github/btrekkie/tree_list/TreeListNode.java create mode 100644 src/com/github/btrekkie/tree_list/test/TreeListTest.java diff --git a/.classpath b/.classpath new file mode 100644 index 000000000..72c2ba61a --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.project b/.project new file mode 100644 index 000000000..ec61c803b --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + DataStructures + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..7341ab168 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..a15aefcfd --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,60 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=false +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=false +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_redundant_type_arguments=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=false +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true +sp_cleanup.use_type_arguments=false diff --git a/README.md b/README.md index bc2c15b8e..0883fb752 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,60 @@ # 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) +`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) + +# Features +* Supports min, max, root, predecessor, successor, insert, remove, rotate, + split, concatenate, create balanced tree, 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. +* 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. +* "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 and 7.0. It might also work in Java 5.0. + +# Limitations: +* 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 +
+/** Red-black tree augmented by the sum of the values in the subtree. */
+public class SumNode extends RedBlackNode {
+    public int value;
+    public int sum;
+
+    public SumNode(int value) {
+        this.value = value;
+    }
+
+    @Override
+    public boolean augment() {
+        int newSum = value + left.sum + right.sum;
+        if (newSum == sum) {
+            return false;
+        } else {
+            sum = newSum;
+            return true;
+        }
+    }
+}
+
+ +# Documentation +For more detailed instructions, check the source code to see the full API and +Javadoc documentation. diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java new file mode 100644 index 000000000..8b5102471 --- /dev/null +++ b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java @@ -0,0 +1,40 @@ +package com.github.btrekkie.arbitrary_order_collection; + +import java.util.Comparator; + +/** + * Provides objects ordered in an arbitrary, but consistent fashion, through the createValue() method. To determine the + * relative order of two values, use ArbitraryOrderValue.compareTo. We may only compare values on which we have not + * called "remove" that were created in the same ArbitraryOrderCollection instance. Note that despite the name, + * ArbitraryOrderCollection does not implement Collection. + */ +/* We implement an ArbitraryOrderCollection using a red-black tree. We order the nodes arbitrarily. + */ +public class ArbitraryOrderCollection { + /** The Comparator for ordering ArbitraryOrderNodes. */ + private static final Comparator NODE_COMPARATOR = new Comparator() { + @Override + public int compare(ArbitraryOrderNode node1, ArbitraryOrderNode node2) { + return 0; + } + }; + + /** The root node of the tree. */ + private ArbitraryOrderNode root = new ArbitraryOrderNode(); + + /** Adds and returns a new value for ordering. */ + public ArbitraryOrderValue createValue() { + ArbitraryOrderNode node = new ArbitraryOrderNode(); + root = root.insert(node, true, NODE_COMPARATOR); + return new ArbitraryOrderValue(node); + } + + /** + * Removes the specified value from this collection. Assumes we obtained the value by calling createValue() on this + * instance of ArbitraryOrderCollection. After calling "remove" on a value, we may no longer compare it to other + * values. + */ + public void remove(ArbitraryOrderValue value) { + root = value.node.remove(); + } +} diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java new file mode 100644 index 000000000..b5d28d9be --- /dev/null +++ b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java @@ -0,0 +1,8 @@ +package com.github.btrekkie.arbitrary_order_collection; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** A node in an ArbitraryOrderCollection tree. See ArbitraryOrderCollection. */ +class ArbitraryOrderNode extends RedBlackNode { + +} diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java new file mode 100644 index 000000000..c12b995ad --- /dev/null +++ b/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java @@ -0,0 +1,19 @@ +package com.github.btrekkie.arbitrary_order_collection; + +/** + * A value in an ArbitraryOrderCollection. To determine the relative order of two values in the same collection, call + * compareTo. + */ +public class ArbitraryOrderValue implements Comparable { + /** The node that establishes this value's relative position. */ + final ArbitraryOrderNode node; + + ArbitraryOrderValue(ArbitraryOrderNode node) { + this.node = node; + } + + @Override + public int compareTo(ArbitraryOrderValue other) { + return node.compareTo(other.node); + } +} diff --git a/src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java b/src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java new file mode 100644 index 000000000..9d855d17d --- /dev/null +++ b/src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java @@ -0,0 +1,72 @@ +package com.github.btrekkie.arbitrary_order_collection.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderCollection; +import com.github.btrekkie.arbitrary_order_collection.ArbitraryOrderValue; + +public class ArbitraryOrderCollectionTest { + /** Tests ArbitraryOrderCollection. */ + @Test + public void test() { + ArbitraryOrderCollection collection = new ArbitraryOrderCollection(); + List values1 = new ArrayList(5); + ArbitraryOrderValue value = collection.createValue(); + assertEquals(0, value.compareTo(value)); + values1.add(value); + for (int i = 0; i < 4; i++) { + values1.add(collection.createValue()); + } + Collections.sort(values1); + List values2 = new ArrayList(10); + for (int i = 0; i < 10; i++) { + value = collection.createValue(); + values2.add(value); + } + for (int i = 0; i < 5; i++) { + collection.remove(values2.get(2 * i)); + } + assertEquals(0, values1.get(0).compareTo(values1.get(0))); + assertTrue(values1.get(0).compareTo(values1.get(1)) < 0); + assertTrue(values1.get(1).compareTo(values1.get(0)) > 0); + assertTrue(values1.get(4).compareTo(values1.get(2)) > 0); + assertTrue(values1.get(0).compareTo(values1.get(4)) < 0); + + collection = new ArbitraryOrderCollection(); + values1 = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + value = collection.createValue(); + values1.add(value); + } + for (int i = 0; i < 500; i++) { + collection.remove(values1.get(2 * i)); + } + values2 = new ArrayList(500); + for (int i = 0; i < 500; i++) { + values2.add(values1.get(2 * i + 1)); + } + for (int i = 0; i < 500; i++) { + values2.get(0).compareTo(values2.get(i)); + } + Collections.sort(values2); + for (int i = 0; i < 500; i++) { + collection.createValue(); + } + for (int i = 0; i < 499; i++) { + assertTrue(values2.get(i).compareTo(values2.get(i + 1)) < 0); + assertTrue(values2.get(i + 1).compareTo(values2.get(i)) > 0); + } + for (int i = 1; i < 500; i++) { + assertEquals(0, values2.get(i).compareTo(values2.get(i))); + assertTrue(values2.get(0).compareTo(values2.get(i)) < 0); + assertTrue(values2.get(i).compareTo(values2.get(0)) > 0); + } + } +} diff --git a/src/com/github/btrekkie/interval_tree/IntervalTree.java b/src/com/github/btrekkie/interval_tree/IntervalTree.java new file mode 100644 index 000000000..d5e88624e --- /dev/null +++ b/src/com/github/btrekkie/interval_tree/IntervalTree.java @@ -0,0 +1,60 @@ +package com.github.btrekkie.interval_tree; + +/** + * An interval tree data structure, which supports adding or removing an interval and finding an arbitrary interval that + * contains a specified value. + */ +/* The interval tree is ordered in ascending order of the start an interval, with ties broken by the end of the + * interval. Each node is augmented with the maximum ending value of an interval in the subtree rooted at the node. + */ +public class IntervalTree { + /** The root node of the tree. */ + private IntervalTreeNode root = IntervalTreeNode.LEAF; + + /** Adds the specified interval to this. */ + public void addInterval(IntervalTreeInterval interval) { + root = root.insert(new IntervalTreeNode(interval), true, null); + } + + /** + * Removes the specified interval from this, if it is present. + * @param interval The interval. + * @return Whether the interval was present. + */ + public boolean removeInterval(IntervalTreeInterval interval) { + IntervalTreeNode node = root; + while (!node.isLeaf()) { + if (interval.start < node.interval.start) { + node = node.left; + } else if (interval.start > node.interval.start) { + node = node.right; + } else if (interval.end < node.interval.end) { + node = node.left; + } else if (interval.end > node.interval.end) { + node = node.right; + } else { + root = node.remove(); + return true; + } + } + return false; + } + + /** + * Returns an aribtrary IntervalTreeInterval in this that contains the specified value. Returns null if there is no + * such interval. + */ + public IntervalTreeInterval findInterval(double value) { + IntervalTreeNode node = root; + while (!node.isLeaf()) { + if (value >= node.interval.start && value <= node.interval.end) { + return node.interval; + } else if (value <= node.left.maxEnd) { + node = node.left; + } else { + node = node.right; + } + } + return null; + } +} diff --git a/src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java b/src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java new file mode 100644 index 000000000..e9f38b9d9 --- /dev/null +++ b/src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java @@ -0,0 +1,28 @@ +package com.github.btrekkie.interval_tree; + +/** + * An inclusive range of values [start, end]. Two intervals are equal if they have the same starting and ending values. + */ +public class IntervalTreeInterval { + /** The smallest value in the range. */ + public final double start; + + /** The largest value in the range. */ + public final double end; + + public IntervalTreeInterval(double start, double end) { + if (start > end) { + throw new IllegalArgumentException("The end of the range must be at most the start"); + } + this.start = start; + this.end = end; + } + + public boolean equals(Object obj) { + if (!(obj instanceof IntervalTreeInterval)) { + return false; + } + IntervalTreeInterval interval = (IntervalTreeInterval)obj; + return start == interval.start && end == interval.end; + } +} diff --git a/src/com/github/btrekkie/interval_tree/IntervalTreeNode.java b/src/com/github/btrekkie/interval_tree/IntervalTreeNode.java new file mode 100644 index 000000000..d75004e77 --- /dev/null +++ b/src/com/github/btrekkie/interval_tree/IntervalTreeNode.java @@ -0,0 +1,68 @@ +package com.github.btrekkie.interval_tree; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** + * A node in an IntervalTree. See the comments for the implementation of IntervalTree. Its compareTo method orders + * nodes as suggested in the comments for the implementation of IntervalTree. + */ +class IntervalTreeNode extends RedBlackNode { + /** The dummy leaf node. */ + public static final IntervalTreeNode LEAF = new IntervalTreeNode(); + + /** The interval stored in this node. */ + public IntervalTreeInterval interval; + + /** The maximum ending value of an interval in the subtree rooted at this node. */ + public double maxEnd; + + public IntervalTreeNode(IntervalTreeInterval interval) { + this.interval = interval; + maxEnd = interval.end; + } + + /** Constructs a new dummy leaf node. */ + private IntervalTreeNode() { + interval = null; + maxEnd = Double.NEGATIVE_INFINITY; + } + + @Override + public boolean augment() { + double newMaxEnd = Math.max(interval.end, Math.max(left.maxEnd, right.maxEnd)); + if (newMaxEnd == maxEnd) { + return false; + } else { + maxEnd = newMaxEnd; + return true; + } + } + + @Override + public void assertNodeIsValid() { + double expectedMaxEnd; + if (isLeaf()) { + expectedMaxEnd = Double.NEGATIVE_INFINITY; + } else { + expectedMaxEnd = Math.max(interval.end, Math.max(left.maxEnd, right.maxEnd)); + } + if (maxEnd != expectedMaxEnd) { + throw new RuntimeException("The node's maxEnd does not match that of the children"); + } + } + + @Override + public int compareTo(IntervalTreeNode other) { + if (interval.start != interval.end) { + return Double.compare(interval.start, other.interval.start); + } else { + return Double.compare(interval.end, other.interval.end); + } + } + + @Override + public void assertSubtreeIsValid() { + super.assertSubtreeIsValid(); + assertOrderIsValid(null); + } +} diff --git a/src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java b/src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java new file mode 100644 index 000000000..cf0dbdfa6 --- /dev/null +++ b/src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java @@ -0,0 +1,48 @@ +package com.github.btrekkie.interval_tree.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.github.btrekkie.interval_tree.IntervalTree; +import com.github.btrekkie.interval_tree.IntervalTreeInterval; + +public class IntervalTreeTest { + /** Tests IntervalTree. */ + @Test + public void test() { + IntervalTree tree = new IntervalTree(); + assertNull(tree.findInterval(0.5)); + assertNull(tree.findInterval(-1)); + tree.addInterval(new IntervalTreeInterval(5, 7)); + tree.addInterval(new IntervalTreeInterval(42, 48)); + tree.addInterval(new IntervalTreeInterval(-1, 2)); + tree.addInterval(new IntervalTreeInterval(6, 12)); + tree.addInterval(new IntervalTreeInterval(21, 23)); + assertTrue(tree.removeInterval(new IntervalTreeInterval(-1, 2))); + assertFalse(tree.removeInterval(new IntervalTreeInterval(-1, 2))); + tree.addInterval(new IntervalTreeInterval(-6, -2)); + assertEquals(new IntervalTreeInterval(6, 12), tree.findInterval(8)); + assertNull(tree.findInterval(0)); + assertEquals(new IntervalTreeInterval(21, 23), tree.findInterval(21)); + assertEquals(new IntervalTreeInterval(42, 48), tree.findInterval(48)); + IntervalTreeInterval interval = tree.findInterval(6.5); + assertTrue(new IntervalTreeInterval(5, 7).equals(interval) || new IntervalTreeInterval(6, 12).equals(interval)); + + tree = new IntervalTree(); + for (int i = 0; i < 500; i++) { + tree.addInterval(new IntervalTreeInterval(2 * i, 2 * i + 1)); + } + for (int i = 0; i < 250; i++) { + tree.removeInterval(new IntervalTreeInterval(4 * i + 2, 4 * i + 3)); + } + assertNull(tree.findInterval(123.5)); + assertEquals(new IntervalTreeInterval(124, 125), tree.findInterval(124.5)); + assertEquals(new IntervalTreeInterval(776, 777), tree.findInterval(776)); + assertEquals(new IntervalTreeInterval(0, 1), tree.findInterval(0.5)); + assertEquals(new IntervalTreeInterval(996, 997), tree.findInterval(997)); + } +} diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java new file mode 100644 index 000000000..8393fafeb --- /dev/null +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -0,0 +1,1224 @@ +package com.github.btrekkie.red_black_node; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashSet; +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. The leaf nodes in a tree are dummy nodes colored black that do + * not contain any values. It is recommended that all of the leaf nodes in a given tree be the same RedBlackNode + * instance, to save space. The root of an empty tree is a leaf node, as opposed to null. + * + * 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(). + * + * 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. + * + * @param The type of node in the tree. For example, we might have "class FooNode extends + * RedBlackNode>". + */ +public abstract class RedBlackNode> implements Comparable { + /** A Comparator that compares Comparable elements using their natural order. */ + private static final Comparator> NATURAL_ORDER = new Comparator>() { + @Override + public int compare(Comparable value1, Comparable value2) { + return value1.compareTo(value2); + } + }; + + /** The parent of this node, if any. "parent" is null if this is a leaf node. */ + public N parent; + + /** The left child of this node. "left" is null if this is a leaf node. */ + public N left; + + /** The right child of this node. "right" is null if this is a leaf node. */ + public N right; + + /** Whether the node is colored red, as opposed to black. */ + public boolean isRed; + + /** + * Sets any augmentation information about the subtree rooted at this node that is stored in this node. For + * example, if we augment each node by subtree size (the number of non-leaf nodes in the subtree), this method would + * set the size field of this node to be equal to the size field of the left child plus the size field of the right + * child plus one. + * + * "Augmentation information" is information that we can compute about a subtree rooted at some node, preferably + * based only on the augmentation information in the node's two children and the information in the node. Examples + * of augmentation information are the sum of the values in a subtree and the number of non-leaf nodes in a subtree. + * Augmentation information may not depend on the colors of the nodes. + * + * This method returns whether the augmentation information in any of the ancestors of this node might have been + * affected by changes in this subtree since the last call to augment(). In the usual case, where the augmentation + * information depends only on the information in this node and the augmentation information in its immediate + * 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 + * before. + * + * This method may assume that this is not a leaf node. It may not assume that the augmentation information stored + * in any of the tree's nodes is correct. However, if the augmentation information stored in all of the node's + * descendants is correct, then the augmentation information stored in this node must be correct after calling + * augment(). + */ + public boolean augment() { + return false; + } + + /** + * Throws a RuntimeException if we detect that this node locally violates any invariants specific to this subclass + * of RedBlackNode. For example, if this stores the size of the subtree rooted at this node, this should throw a + * RuntimeException if the size field of this is not equal to the size field of the left child plus the size field + * of the right child plus one. Note that we may call this on a leaf node. + * + * assertSubtreeIsValid() calls assertNodeIsValid() on each node, or at least starts to do so until it detects a + * problem. assertNodeIsValid() should assume the node is in a tree that satisfies all properties common to all + * red-black trees, as assertSubtreeIsValid() is responsible for such checks. assertNodeIsValid() should be + * "downward-looking", i.e. it should ignore any information in "parent", and it should be "local", i.e. it should + * only check a constant number of descendants. To include "global" checks, such as verifying the BST property + * concerning ordering, override assertSubtreeIsValid(). assertOrderIsValid is useful for checking the BST + * property. + */ + public void assertNodeIsValid() { + + } + + /** Returns whether this is a leaf node. */ + public boolean isLeaf() { + return left == null; + } + + /** Returns the root of the tree that contains this node. */ + public N root() { + @SuppressWarnings("unchecked") + N node = (N)this; + while (node.parent != null) { + node = node.parent; + } + return node; + } + + /** Returns the first node in the subtree rooted at this node, if any. */ + public N min() { + if (isLeaf()) { + return null; + } + @SuppressWarnings("unchecked") + N node = (N)this; + while (!node.left.isLeaf()) { + node = node.left; + } + return node; + } + + /** Returns the last node in the subtree rooted at this node, if any. */ + public N max() { + if (isLeaf()) { + return null; + } + @SuppressWarnings("unchecked") + N node = (N)this; + while (!node.right.isLeaf()) { + node = node.right; + } + return node; + } + + /** Returns the node immediately before this in the tree that contains this node, if any. */ + public N predecessor() { + if (!left.isLeaf()) { + N node; + for (node = left; !node.right.isLeaf(); node = node.right); + return node; + } else if (parent == null) { + return null; + } else { + @SuppressWarnings("unchecked") + N node = (N)this; + while (node.parent != null && node.parent.left == node) { + node = node.parent; + } + return node.parent; + } + } + + /** Returns the node immediately after this in the tree that contains this node, if any. */ + public N successor() { + if (!right.isLeaf()) { + N node; + for (node = right; !node.left.isLeaf(); node = node.left); + return node; + } else if (parent == null) { + return null; + } else { + @SuppressWarnings("unchecked") + N node = (N)this; + while (node.parent != null && node.parent.right == node) { + node = node.parent; + } + return node.parent; + } + } + + /** + * Performs a left rotation about this node. This method assumes that !right.isLeaf(). It calls augment() on this + * node and on its resulting parent. + * @return The return value from calling augment() on the resulting parent. + */ + public boolean rotateLeft() { + if (right.isLeaf()) { + throw new IllegalArgumentException("The right child is a leaf"); + } + N newParent = right; + right = newParent.left; + @SuppressWarnings("unchecked") + N nThis = (N)this; + if (!right.isLeaf()) { + right.parent = nThis; + } + newParent.parent = parent; + parent = newParent; + newParent.left = nThis; + if (newParent.parent != null) { + if (newParent.parent.left == this) { + newParent.parent.left = newParent; + } else { + newParent.parent.right = newParent; + } + } + augment(); + return newParent.augment(); + } + + /** + * Performs a right rotation about this node. This method assumes that !left.isLeaf(). It calls augment() on this + * node and on its resulting parent. + * @return The return value from calling augment() on the resulting parent. + */ + public boolean rotateRight() { + if (left.isLeaf()) { + throw new IllegalArgumentException("The left child is a leaf"); + } + N newParent = left; + left = newParent.right; + @SuppressWarnings("unchecked") + N nThis = (N)this; + if (!left.isLeaf()) { + left.parent = nThis; + } + newParent.parent = parent; + parent = newParent; + newParent.right = nThis; + if (newParent.parent != null) { + if (newParent.parent.left == this) { + newParent.parent.left = newParent; + } else { + newParent.parent.right = newParent; + } + } + augment(); + return newParent.augment(); + } + + /** + * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements + * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be + * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and + * rotateRight(). + * @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment(). + */ + public void fixInsertion(boolean augment) { + if (!isRed) { + throw new IllegalArgumentException("The node must be red"); + } + boolean changed; + if (augment) { + changed = augment(); + } else { + changed = false; + } + + RedBlackNode node = this; + while (node.parent != null && node.parent.isRed) { + N parent = node.parent; + N grandparent = parent.parent; + if (grandparent.left.isRed && grandparent.right.isRed) { + grandparent.left.isRed = false; + grandparent.right.isRed = false; + grandparent.isRed = true; + if (changed) { + changed = parent.augment(); + if (changed) { + changed = grandparent.augment(); + } + } + node = grandparent; + } else { + if (parent.left == node) { + if (grandparent.right == parent) { + parent.rotateRight(); + node = parent; + parent = node.parent; + } + } else if (grandparent.left == parent) { + parent.rotateLeft(); + node = parent; + parent = node.parent; + } + if (parent.left == node) { + boolean grandparentChanged = grandparent.rotateRight(); + if (augment) { + changed = grandparentChanged; + } + } else { + boolean grandparentChanged = grandparent.rotateLeft(); + if (augment) { + changed = grandparentChanged; + } + } + parent.isRed = false; + grandparent.isRed = true; + node = parent; + break; + } + } + + if (node.parent == null) { + node.isRed = false; + } + if (changed) { + for (node = node.parent; node != null; node = node.parent) { + if (!node.augment()) { + break; + } + } + } + } + + /** + * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements + * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be + * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and + * rotateRight(). + */ + public void fixInsertion() { + fixInsertion(true); + } + + /** Returns a Comparator that compares instances of N using their natural order, as in N.compare. */ + private Comparator naturalOrder() { + @SuppressWarnings("unchecked") + Comparator comparator = (Comparator)NATURAL_ORDER; + return comparator; + } + + /** + * 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. + * + * If it is not efficient or convenient for a subclass to find the location for a node using a Comparator, then it + * 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 + * 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.compare. + * @return The root of the resulting tree. + */ + public N insert(N newNode, boolean allowDuplicates, Comparator comparator) { + if (parent != null) { + throw new IllegalArgumentException("This is not the root of a tree"); + } + @SuppressWarnings("unchecked") + N nThis = (N)this; + if (isLeaf()) { + newNode.isRed = false; + newNode.left = nThis; + newNode.right = nThis; + newNode.parent = null; + newNode.augment(); + return newNode; + } + if (comparator == null) { + comparator = naturalOrder(); + } + + N node = nThis; + int comparison; + while (true) { + comparison = comparator.compare(newNode, node); + if (comparison < 0) { + if (!node.left.isLeaf()) { + node = node.left; + } else { + newNode.left = node.left; + newNode.right = node.left; + node.left = newNode; + newNode.parent = node; + break; + } + } else if (comparison > 0 || allowDuplicates) { + if (!node.right.isLeaf()) { + node = node.right; + } else { + newNode.left = node.right; + newNode.right = node.right; + node.right = newNode; + newNode.parent = node; + break; + } + } else { + newNode.parent = null; + return nThis; + } + } + newNode.isRed = true; + newNode.fixInsertion(); + return root(); + } + + /** + * Moves this node to its successor's former position in the tree and vice versa, i.e. sets the "left", "right", + * "parent", and isRed fields of each. + * @return The node with which we swapped. + */ + private N swapWithSuccessor() { + N replacement = successor(); + boolean oldReplacementIsRed = replacement.isRed; + N oldReplacementLeft = replacement.left; + N oldReplacementRight = replacement.right; + N oldReplacementParent = replacement.parent; + + replacement.isRed = isRed; + replacement.left = left; + replacement.right = right; + replacement.parent = parent; + if (parent != null) { + if (parent.left == this) { + parent.left = replacement; + } else { + parent.right = replacement; + } + } + + @SuppressWarnings("unchecked") + N nThis = (N)this; + isRed = oldReplacementIsRed; + left = oldReplacementLeft; + right = oldReplacementRight; + if (oldReplacementParent == this) { + parent = replacement; + parent.right = nThis; + } else { + parent = oldReplacementParent; + parent.left = nThis; + } + + replacement.right.parent = replacement; + if (!replacement.left.isLeaf()) { + replacement.left.parent = replacement; + } + if (!right.isLeaf()) { + right.parent = nThis; + } + return replacement; + } + + /** + * Performs red-black deletion fixup. To be more precise, this fixes a tree that satisfies all of the requirements + * of red-black trees, except that all paths from the root to a leaf that pass through the sibling of this node have + * one fewer black node than all other root-to-leaf paths. + */ + private void fixSiblingDeletion() { + RedBlackNode sibling = this; + boolean changed = true; + while (true) { + N parent = sibling.parent; + if (sibling.isRed) { + parent.isRed = true; + sibling.isRed = false; + if (parent.left == sibling) { + changed = parent.rotateRight(); + sibling = parent.left; + } else { + changed = parent.rotateLeft(); + sibling = parent.right; + } + } else if (!sibling.left.isRed && !sibling.right.isRed) { + sibling.isRed = true; + if (parent.isRed) { + parent.isRed = false; + break; + } else { + if (changed) { + changed = parent.augment(); + } + N grandparent = parent.parent; + if (grandparent == null) { + break; + } else if (grandparent.left == parent) { + sibling = grandparent.right; + } else { + sibling = grandparent.left; + } + } + } else { + if (sibling == parent.left) { + if (!sibling.left.isRed) { + sibling.rotateLeft(); + sibling = sibling.parent; + } + } else if (!sibling.right.isRed) { + sibling.rotateRight(); + sibling = sibling.parent; + } + sibling.isRed = parent.isRed; + parent.isRed = false; + if (sibling == parent.left) { + sibling.left.isRed = false; + changed = parent.rotateRight(); + } else { + sibling.right.isRed = false; + changed = parent.rotateLeft(); + } + break; + } + } + + if (changed) { + for (N parent = sibling.parent; parent != null; parent = parent.parent) { + if (!parent.augment()) { + break; + } + } + } + } + + /** + * Removes this node from the tree that contains it. The effect of this method on the fields of this node is + * unspecified. This method is more efficient than remove() if augment() might return false. + * + * If the node has two children, we begin by moving the node's successor to its former position, by changing its + * "left", "right", "parent", and "isBlack" fields. + */ + public void removeWithoutGettingRoot() { + N replacement; + if (left.isLeaf() || right.isLeaf()) { + replacement = null; + } else { + replacement = swapWithSuccessor(); + } + + N child; + if (!left.isLeaf()) { + child = left; + } else if (!right.isLeaf()) { + child = right; + } else { + child = null; + } + + if (child != null) { + child.parent = parent; + if (parent != null) { + if (parent.left == this) { + parent.left = child; + } else { + parent.right = child; + } + } + parent = null; + child.isRed = false; + if (child.parent != null) { + N parent; + for (parent = child.parent; parent != null; parent = parent.parent) { + if (!parent.augment()) { + break; + } + } + } + } else if (parent != null) { + N leaf = left; + N parent = this.parent; + N sibling; + if (parent.left == this) { + parent.left = leaf; + sibling = parent.right; + } else { + parent.right = leaf; + sibling = parent.left; + } + this.parent = null; + if (!isRed) { + RedBlackNode siblingNode = sibling; + siblingNode.fixSiblingDeletion(); + } else { + while (parent != null) { + if (!parent.augment()) { + break; + } + parent = parent.parent; + } + } + } + + if (replacement != null) { + replacement.augment(); + for (N parent = replacement.parent; parent != null; parent = parent.parent) { + if (!parent.augment()) { + break; + } + } + } + } + + /** + * Removes this node from the tree that contains it. The effect of this method on the fields of this node is + * unspecified. + * + * If the node has two children, we begin by moving the node's successor to its former position, by changing its + * "left", "right", "parent", and "isBlack" fields. + * + * @return The root of the resulting tree. + */ + public N remove() { + // Find an arbitrary non-leaf node in the tree other than this node + N node; + if (parent != null) { + node = parent; + } else if (!left.isLeaf()) { + node = left; + } else if (!right.isLeaf()) { + node = right; + } else { + return left; + } + + removeWithoutGettingRoot(); + return node.root(); + } + + /** + * Returns the root of a perfectly height-balanced subtree containing the next "size" nodes from "iterator", 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. + * @param iterator The nodes. + * @param size The number of nodes. + * @param height The "height" of the subtree's root node above the deepest leaf in the tree that contains it. Since + * insertion fixup is slow if there are too many red nodes and deleteion fixup is slow if there are too few red + * nodes, we compromise and have red nodes at every fourth level. We color a node red iff its "height" is equal + * to 1 mod 4. + * @return The root of the subtree. + */ + private static > N createTree( + Iterator iterator, int size, int height, N leaf) { + if (size == 0) { + return leaf; + } else { + N left = createTree(iterator, (size - 1) / 2, height - 1, leaf); + N node = iterator.next(); + N right = createTree(iterator, size / 2, height - 1, leaf); + node.isRed = height % 4 == 1; + node.left = left; + node.right = right; + if (!left.isLeaf()) { + left.parent = node; + } + if (!right.isLeaf()) { + right.parent = node; + } + node.augment(); + return node; + } + } + + /** + * 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. + * @param nodes The nodes. + * @param leaf The leaf node. + * @return The root of the tree. + */ + public static > N createTree(Collection nodes, N leaf) { + int size = nodes.size(); + if (size == 0) { + return leaf; + } + int height = 0; + for (int subtreeSize = size; subtreeSize > 0; subtreeSize /= 2) { + height++; + } + N node = createTree(nodes.iterator(), size, height, leaf); + node.isRed = false; + return node; + } + + /** + * Concatenates to the end of the tree rooted at this node. To be precise, given that all of the nodes in this + * precede the node "pivot", which precedes all of the nodes in "last", this returns the root of a tree containing + * all of these nodes. This method destroys the trees rooted at "this" and "last". We treat "pivot" as a solitary + * node that does not belong to any tree, and we ignore its initial "parent", "left", "right", and isRed fields. + * This method assumes that this node and "last" are the roots of their respective trees. + * + * This method takes O(log N) time. It is more efficient than inserting "pivot" and then calling concatenate(last). + * It is considerably more efficient than inserting "pivot" and all of the nodes in "last". + */ + public N concatenate(N last, N pivot) { + // If the black height of "first", where first = this, is less than or equal to that of "last", starting at the + // root of "last", we keep going left until we reach a black node whose black height is equal to that of + // "first". Then, we make "pivot" the parent of that node and of "first", coloring it red, and perform + // insertion fixup on the pivot. If the black height of "first" is greater than that of "last", we do the mirror + // image of the above. + + if (parent != null) { + throw new IllegalArgumentException("This is not the root of a tree"); + } + if (last.parent != null) { + throw new IllegalArgumentException("\"last\" is not the root of a tree"); + } + + // Compute the black height of the trees + int firstBlackHeight = 0; + @SuppressWarnings("unchecked") + N first = (N)this; + for (N node = first; node != null; node = node.right) { + if (!node.isRed) { + firstBlackHeight++; + } + } + int lastBlackHeight = 0; + for (N node = last; node != null; node = node.right) { + if (!node.isRed) { + lastBlackHeight++; + } + } + + // Identify the children and parent of pivot + N firstChild = first; + N lastChild = last; + N parent; + if (firstBlackHeight <= lastBlackHeight) { + parent = null; + while (lastBlackHeight > firstBlackHeight) { + if (!lastChild.isRed) { + lastBlackHeight--; + } + parent = lastChild; + lastChild = lastChild.left; + } + if (lastChild.isRed) { + parent = lastChild; + lastChild = lastChild.left; + } + } else { + parent = null; + while (firstBlackHeight > lastBlackHeight) { + if (!firstChild.isRed) { + firstBlackHeight--; + } + parent = firstChild; + firstChild = firstChild.right; + } + if (firstChild.isRed) { + parent = firstChild; + firstChild = firstChild.right; + } + } + + // Add "pivot" to the tree + pivot.isRed = true; + pivot.parent = parent; + if (parent != null) { + if (parent.left == lastChild) { + parent.left = pivot; + } else { + parent.right = pivot; + } + } + pivot.left = firstChild; + if (!firstChild.isLeaf()) { + firstChild.parent = pivot; + } + pivot.right = lastChild; + if (!lastChild.isLeaf()) { + lastChild.parent = pivot; + } + + // Perform insertion fixup + pivot.fixInsertion(); + + return pivot.root(); + } + + /** + * Concatenates the tree rooted at "last" to the end of the tree rooted at this node. To be precise, given that all + * of the nodes in this precede all of the nodes in "last", this returns the root of a tree containing all of these + * nodes. This method destroys the trees rooted at "this" and "last". It assumes that this node and "last" are the + * roots of their respective trees. This method takes O(log N) time. It is considerably more efficient than + * inserting all of the nodes in "last". + */ + public N concatenate(N last) { + if (isLeaf()) { + return last; + } else if (parent != null) { + throw new IllegalArgumentException("This is not the root of a tree"); + } else if (last.isLeaf()) { + @SuppressWarnings("unchecked") + N nThis = (N)this; + return nThis; + } else { + N node = last.min(); + last = node.remove(); + return concatenate(last, node); + } + } + + /** + * Splits the tree rooted at this node into two trees, so that the first element of the return value is the root of + * a tree consisting of the nodes that were before the specified node, and the second element of the return value is + * the root of a tree consisting of the nodes that were equal to or after the specified node. This method assumes + * that this node is the root. It takes O(log N) time. It is considerably more efficient than removing all of the + * elements after splitNode and then creating a new tree from those nodes. + * @return An array consisting of the resulting trees. + */ + public N[] split(N splitNode) { + // To split the tree, we accumulate a pre-split tree and a post-split tree. We walk down the tree toward the + // position where we are splitting. Whenever we go left, we concatenate the right subtree with the post-split + // tree, and whenever we go right, we concatenate the pre-split tree with the left subtree. We use the + // concatenation algorithm described in concatenate(Object, Object). For the pivot, we use the last node where + // we went left in the case of a left move, and the last node where we went right in the case of a right move. + // + // The method uses the following variables: + // + // node: The current node in our walk down the tree. + // first: A node on the right spine of the pre-split tree. At the beginning of each iteration, it is the black + // node with the same black height as "node". If the pre-split tree is empty, this is null instead. + // firstParent: The parent of "first". If the pre-split tree is empty, this is null. Otherwise, this is the + // same as first.parent, unless first.isLeaf(). + // 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, + // but for the post-split tree. + if (parent != null) { + throw new IllegalArgumentException("This is not the root of a tree"); + } + + // Create an array containing the path from the root to splitNode + int depth = 1; + N parent; + for (parent = splitNode; parent.parent != null; parent = parent.parent) { + depth++; + } + if (parent != this) { + throw new IllegalArgumentException("The split node does not belong to this tree"); + } + @SuppressWarnings("unchecked") + N[] path = (N[])Array.newInstance(getClass(), depth); + for (parent = splitNode; parent != null; parent = parent.parent) { + depth--; + path[depth] = parent; + } + + @SuppressWarnings("unchecked") + N node = (N)this; + N first = null; + N firstParent = null; + N last = null; + N lastParent = null; + N firstPivot = null; + N lastPivot = null; + while (!node.isLeaf()) { + boolean advanceFirst = !node.isRed && firstPivot != null; + boolean advanceLast = !node.isRed && lastPivot != null; + if ((depth + 1 < path.length && path[depth + 1] == node.left) || depth + 1 == path.length) { + // Left move + if (lastPivot == null) { + // The post-split tree is empty + last = node.right; + last.parent = null; + if (last.isRed) { + last.isRed = false; + lastParent = last; + last = last.left; + } + } else { + // Concatenate node.right and the post-split tree + if (node.right.isRed) { + node.right.isRed = false; + } else if (!node.isRed) { + lastParent = last; + last = last.left; + if (last.isRed) { + lastParent = last; + last = last.left; + } + advanceLast = false; + } + lastPivot.isRed = true; + lastPivot.parent = lastParent; + if (lastParent != null) { + lastParent.left = lastPivot; + } + lastPivot.left = node.right; + if (!lastPivot.left.isLeaf()) { + lastPivot.left.parent = lastPivot; + } + lastPivot.right = last; + if (!last.isLeaf()) { + last.parent = lastPivot; + } + last = lastPivot.left; + lastParent = lastPivot; + lastPivot.fixInsertion(false); + } + lastPivot = node; + node = node.left; + } else { + // Right move + if (firstPivot == null) { + // The pre-split tree is empty + first = node.left; + first.parent = null; + if (first.isRed) { + first.isRed = false; + firstParent = first; + first = first.right; + } + } else { + // Concatenate the post-split tree and node.left + if (node.left.isRed) { + node.left.isRed = false; + } else if (!node.isRed) { + firstParent = first; + first = first.right; + if (first.isRed) { + firstParent = first; + first = first.right; + } + advanceFirst = false; + } + firstPivot.isRed = true; + firstPivot.parent = firstParent; + if (firstParent != null) { + firstParent.right = firstPivot; + } + firstPivot.right = node.left; + if (!firstPivot.right.isLeaf()) { + firstPivot.right.parent = firstPivot; + } + firstPivot.left = first; + if (!first.isLeaf()) { + first.parent = firstPivot; + } + first = firstPivot.right; + firstParent = firstPivot; + firstPivot.fixInsertion(false); + } + firstPivot = node; + node = node.right; + } + + depth++; + + // Update "first" and "last" to be the nodes at the proper black height + if (advanceFirst) { + firstParent = first; + first = first.right; + if (first.isRed) { + firstParent = first; + first = first.right; + } + } + if (advanceLast) { + lastParent = last; + last = last.left; + if (last.isRed) { + lastParent = last; + last = last.left; + } + } + } + + // Add firstPivot to the pre-split tree + N leaf = node; + if (first == null) { + first = leaf; + } else { + firstPivot.isRed = true; + firstPivot.parent = firstParent; + if (firstParent != null) { + firstParent.right = firstPivot; + } + firstPivot.left = leaf; + firstPivot.right = leaf; + firstPivot.fixInsertion(false); + for (first = firstPivot; first.parent != null; first = first.parent) { + first.augment(); + } + first.augment(); + } + + // Add lastPivot to the post-split tree + if (last == null) { + last = leaf; + } else { + lastPivot.isRed = true; + lastPivot.parent = lastParent; + if (lastParent != null) { + lastParent.left = lastPivot; + } + lastPivot.left = leaf; + lastPivot.right = leaf; + lastPivot.fixInsertion(false); + for (last = lastPivot; last.parent != null; last = last.parent) { + last.augment(); + } + last.augment(); + } + + @SuppressWarnings("unchecked") + N[] result = (N[])Array.newInstance(getClass(), 2); + result[0] = first; + result[1] = last; + return result; + } + + /** + * 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". + * + * 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. + */ + public int compareTo(N other) { + // The algorithm operates as follows: compare the depth of this node to that of "other". If the depth of + // "other" is greater, keep moving up from "other" until we find the ancestor at the same depth. Then, keep + // moving up from "this" and from that node until we reach the lowest common ancestor. The node that arrived + // from the left child of the common ancestor is earlier. The algorithm is analogous if the depth of "other" is + // not greater. + if (this == other) { + return 0; + } + + // Compute the depth of each node + int depth = 0; + RedBlackNode parent; + for (parent = this; parent.parent != null; parent = parent.parent) { + depth++; + } + int otherDepth = 0; + N otherParent; + for (otherParent = other; otherParent.parent != null; otherParent = otherParent.parent) { + otherDepth++; + } + + // Go up to nodes of the same depth + if (depth < otherDepth) { + otherParent = other; + for (int i = otherDepth - 1; i > depth; i--) { + otherParent = otherParent.parent; + } + if (otherParent.parent != this) { + otherParent = otherParent.parent; + } else if (left == otherParent) { + return 1; + } else { + return -1; + } + parent = this; + } else if (depth > otherDepth) { + parent = this; + for (int i = depth - 1; i > otherDepth; i--) { + parent = parent.parent; + } + if (parent.parent != other) { + parent = parent.parent; + } else if (other.left == parent) { + return -1; + } else { + return 1; + } + otherParent = other; + } else { + parent = this; + otherParent = other; + } + + // Keep going up until we reach the lowest common ancestor + while (parent.parent != otherParent.parent) { + parent = parent.parent; + otherParent = otherParent.parent; + } + if (parent.parent == null) { + throw new IllegalArgumentException("The nodes do not belong to the same tree"); + } + if (parent.parent.left == parent) { + return -1; + } else { + return 1; + } + } + + /** Throws a RuntimeException if the RedBlackNode fields of this are not correct for a leaf node. */ + private void assertIsValidLeaf() { + if (left != null || right != null || parent != null || isRed) { + throw new RuntimeException("A leaf node's \"left\", \"right\", \"parent\", or isRed field is incorrect"); + } + } + + /** + * 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. + * @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 + * the subtree rooted at this node to "visited". + */ + private void assertSubtreeIsValidRedBlack(int blackHeight, Set> visited) { + @SuppressWarnings("unchecked") + N nThis = (N)this; + if (left == null || right == null) { + assertIsValidLeaf(); + if (blackHeight != 1) { + throw new RuntimeException("Not all root-to-leaf paths have the same number of black nodes"); + } + return; + } else if (!visited.add(new Reference(nThis))) { + throw new RuntimeException("The tree contains a repeated non-leaf node"); + } else { + int childBlackHeight; + if (isRed) { + if (!left.isLeaf() && left.isRed) { + throw new RuntimeException("A red node has a red child"); + } + if (!right.isLeaf() && right.isRed) { + throw new RuntimeException("A red node has a red child"); + } + childBlackHeight = blackHeight; + } else if (blackHeight == 0) { + throw new RuntimeException("Not all root-to-leaf paths have the same number of black nodes"); + } else { + childBlackHeight = blackHeight - 1; + } + + if (!left.isLeaf() && left.parent != this) { + throw new RuntimeException("left.parent != this"); + } + if (!right.isLeaf() && right.parent != this) { + throw new RuntimeException("right.parent != this"); + } + RedBlackNode leftNode = left; + RedBlackNode rightNode = right; + leftNode.assertSubtreeIsValidRedBlack(childBlackHeight, visited); + rightNode.assertSubtreeIsValidRedBlack(childBlackHeight, visited); + } + } + + /** Calls assertNodeIsValid() on every node in the subtree rooted at this node. */ + private void assertNodesAreValid() { + assertNodeIsValid(); + if (left != null) { + RedBlackNode leftNode = left; + RedBlackNode rightNode = right; + leftNode.assertNodesAreValid(); + rightNode.assertNodesAreValid(); + } + } + + /** + * 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(). + */ + public void assertSubtreeIsValidRedBlack() { + if (isLeaf()) { + assertIsValidLeaf(); + } else { + if (parent == null && isRed) { + throw new RuntimeException("The root is red"); + } + + // Compute the black height of the tree + Set> nodes = new HashSet>(); + int blackHeight = 0; + @SuppressWarnings("unchecked") + N node = (N)this; + while (node != null) { + if (!nodes.add(new Reference(node))) { + throw new RuntimeException("The tree contains a repeated non-leaf node"); + } + if (!node.isRed) { + blackHeight++; + } + node = node.left; + } + + assertSubtreeIsValidRedBlack(blackHeight, new HashSet>()); + } + } + + /** + * Throws a RuntimeException if we detect a problem with the subtree rooted at this node, such as a red child of a + * red node or a non-leaf descendant "node" for which node.left.parent != node. This method is useful for + * debugging. RedBlackNode subclasses may want to override assertSubtreeIsValid() to call assertOrderIsValid. + */ + public void assertSubtreeIsValid() { + assertSubtreeIsValidRedBlack(); + assertNodesAreValid(); + } + + /** + * Throws a RuntimeException if the nodes in the subtree rooted at this node are not in the specified order or they + * do not lie in the specified range. Assumes that the subtree rooted at this node is a valid binary tree, i.e. it + * has no repeated nodes other than leaf nodes. + * @param comparator A comparator indicating how the nodes should be ordered. + * @param start The lower limit for nodes in the subtree, if any. + * @param end The upper limit for nodes in the subtree, if any. + */ + private void assertOrderIsValid(Comparator comparator, N start, N end) { + if (!isLeaf()) { + @SuppressWarnings("unchecked") + N nThis = (N)this; + if (start != null && comparator.compare(nThis, start) < 0) { + throw new RuntimeException("The nodes are not ordered correctly"); + } + if (end != null && comparator.compare(nThis, end) > 0) { + throw new RuntimeException("The nodes are not ordered correctly"); + } + RedBlackNode leftNode = left; + RedBlackNode rightNode = right; + leftNode.assertOrderIsValid(comparator, start, nThis); + rightNode.assertOrderIsValid(comparator, nThis, end); + } + } + + /** + * Throws a RuntimeException if the nodes in the subtree rooted at this node are not in the specified order. + * Assumes that this is a valid binary tree, i.e. there are no repeated nodes other than leaf nodes. This method is + * useful for debugging. RedBlackNode subclasses may want to override assertSubtreeIsValid() to call + * assertOrderIsValid. + * @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes' + * natural order, as in N.compare. + */ + public void assertOrderIsValid(Comparator comparator) { + if (comparator == null) { + comparator = naturalOrder(); + } + assertOrderIsValid(comparator, null, null); + } +} diff --git a/src/com/github/btrekkie/red_black_node/Reference.java b/src/com/github/btrekkie/red_black_node/Reference.java new file mode 100644 index 000000000..843053820 --- /dev/null +++ b/src/com/github/btrekkie/red_black_node/Reference.java @@ -0,0 +1,28 @@ +package com.github.btrekkie.red_black_node; + +/** + * Wraps a value using reference equality. In other words, two references are equal only if their values are the same + * object instance, as in ==. + * @param  The type of value. + */ +class Reference { + /** The value this wraps. */ + private final T value; + + public Reference(T value) { + this.value = value; + } + + public boolean equals(Object obj) { + if (!(obj instanceof Reference)) { + return false; + } + Reference reference = (Reference)obj; + return value == reference.value; + } + + @Override + public int hashCode() { + return System.identityHashCode(value); + } +} diff --git a/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java b/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java new file mode 100644 index 000000000..6059745aa --- /dev/null +++ b/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java @@ -0,0 +1,171 @@ +package com.github.btrekkie.red_black_node.test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Comparator; + +import org.junit.Test; + +/** + * Tests RedBlackNode. Most of the testing for RedBlackNode takes place in TreeListTest, IntervalTreeTest, and + * ArbitraryOrderCollectionTest, which test realistic use cases of RedBlackNode. TreeListTest tests most of the + * RedBlackNode methods, while IntervalTreeTest tests non-structural augmentation and the "insert" method, + * ArbitraryOrderCollectionTest tests compareTo, and RedBlackNodeTest tests assertSubtreeIsValid() and + * assertOrderIsValid. + */ +public class RedBlackNodeTest { + /** + * Returns whether the subtree rooted at the specified node is valid, as in TestRedBlackNode.assertSubtreeIsValid(). + */ + private boolean isSubtreeValid(TestRedBlackNode node) { + try { + node.assertSubtreeIsValid(); + return true; + } catch (RuntimeException exception) { + return false; + } + } + + /** + * Returns whether the nodes in the subtree rooted at the specified node are ordered correctly, as in + * TestRedBlackNode.assertOrderIsValid. + * @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes' + * natural ordering, as in TestRedBlackNode.compare. + */ + private boolean isOrderValid(TestRedBlackNode node, Comparator comparator) { + try { + node.assertOrderIsValid(null); + return true; + } catch (RuntimeException exception) { + return false; + } + } + + /** Tests RedBlackNode.assertSubtreeIsValid() and RedBlackNode.assertOrderIsValid. */ + @Test + public void testAssertIsValid() { + // Create a perfectly balanced tree of height 3 + TestRedBlackNode node0 = new TestRedBlackNode(0); + TestRedBlackNode node1 = new TestRedBlackNode(1); + TestRedBlackNode node2 = new TestRedBlackNode(2); + TestRedBlackNode node3 = new TestRedBlackNode(3); + TestRedBlackNode node4 = new TestRedBlackNode(4); + TestRedBlackNode node5 = new TestRedBlackNode(5); + TestRedBlackNode node6 = new TestRedBlackNode(6); + node0.parent = node1; + node0.left = TestRedBlackNode.LEAF; + node0.right = TestRedBlackNode.LEAF; + node1.parent = node3; + node1.left = node0; + node1.right = node2; + node1.isRed = true; + node2.parent = node1; + node2.left = TestRedBlackNode.LEAF; + node2.right = TestRedBlackNode.LEAF; + node3.left = node1; + node3.right = node5; + node4.parent = node5; + node4.left = TestRedBlackNode.LEAF; + node4.right = TestRedBlackNode.LEAF; + node5.parent = node3; + node5.left = node4; + node5.right = node6; + node5.isRed = true; + node6.parent = node5; + node6.left = TestRedBlackNode.LEAF; + node6.right = TestRedBlackNode.LEAF; + + node3.left = node3; + node3.right = node3; + node3.parent = node3; + assertFalse(isSubtreeValid(node3)); + node3.left = node1; + node3.right = node5; + node3.parent = null; + + node0.parent = node3; + assertFalse(isSubtreeValid(node3)); + node0.parent = node1; + + node1.right = node0; + assertFalse(isSubtreeValid(node3)); + node1.right = node2; + + node5.isRed = false; + assertFalse(isSubtreeValid(node3)); + assertTrue(isSubtreeValid(node5)); + node5.isRed = true; + + node3.isRed = true; + assertFalse(isSubtreeValid(node3)); + assertTrue(isSubtreeValid(node5)); + node3.isRed = false; + + node0.isRed = true; + node2.isRed = true; + node4.isRed = true; + node6.isRed = true; + assertFalse(isSubtreeValid(node3)); + node0.isRed = false; + node2.isRed = false; + node4.isRed = false; + node6.isRed = false; + + TestRedBlackNode.LEAF.isRed = true; + assertFalse(isSubtreeValid(node3)); + TestRedBlackNode.LEAF.isRed = false; + + TestRedBlackNode.LEAF.isValid = false; + assertFalse(isSubtreeValid(node3)); + assertFalse(isSubtreeValid(TestRedBlackNode.LEAF)); + TestRedBlackNode.LEAF.isValid = true; + + node1.isValid = false; + assertFalse(isSubtreeValid(node3)); + node1.isValid = true; + + node3.value = 2; + node2.value = 3; + assertFalse(isOrderValid(node3, null)); + assertFalse( + isOrderValid(node3, new Comparator() { + @Override + public int compare(TestRedBlackNode node1, TestRedBlackNode node2) { + return node1.value - node2.value; + } + })); + node3.value = 3; + node2.value = 2; + + node2.value = 4; + node4.value = 2; + assertFalse(isOrderValid(node3, null)); + node2.value = 2; + node4.value = 4; + + node0.value = 1; + node1.value = 0; + assertFalse(isOrderValid(node3, null)); + node0.value = 0; + node1.value = 1; + + // Do all of the assertions for which the tree is supposed to be valid at the end, to make sure we didn't make a + // mistake undoing any of the modifications + assertTrue(isSubtreeValid(node3)); + assertTrue(isSubtreeValid(node1)); + assertTrue(isSubtreeValid(node0)); + assertTrue(isSubtreeValid(TestRedBlackNode.LEAF)); + assertTrue(isOrderValid(node3, null)); + assertTrue(isOrderValid(node1, null)); + assertTrue(isOrderValid(node0, null)); + assertTrue(isOrderValid(TestRedBlackNode.LEAF, null)); + assertTrue( + isOrderValid(node3, new Comparator() { + @Override + public int compare(TestRedBlackNode node1, TestRedBlackNode node2) { + return node1.value - node2.value; + } + })); + } +} diff --git a/src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java b/src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java new file mode 100644 index 000000000..8e3b78cd8 --- /dev/null +++ b/src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java @@ -0,0 +1,36 @@ +package com.github.btrekkie.red_black_node.test; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** A RedBlackNode for RedBlackNodeTest. */ +class TestRedBlackNode extends RedBlackNode { + /** The dummy leaf node. */ + public static final TestRedBlackNode LEAF = new TestRedBlackNode(); + + /** The value stored in this node. "value" is unspecified if this is a leaf node. */ + public int value; + + /** Whether this node is considered valid, as in assertNodeIsValid(). */ + public boolean isValid = true; + + public TestRedBlackNode(int value) { + this.value = value; + } + + /** Constructs a new dummy leaf node. */ + private TestRedBlackNode() { + + } + + @Override + public void assertNodeIsValid() { + if (!isValid) { + throw new RuntimeException("isValid is false"); + } + } + + @Override + public int compareTo(TestRedBlackNode other) { + return value - other.value; + } +} diff --git a/src/com/github/btrekkie/tree_list/TreeList.java b/src/com/github/btrekkie/tree_list/TreeList.java new file mode 100644 index 000000000..b5de0ccbc --- /dev/null +++ b/src/com/github/btrekkie/tree_list/TreeList.java @@ -0,0 +1,461 @@ +package com.github.btrekkie.tree_list; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** + * Implements a list using a self-balancing binary search tree augmented by subtree size. The benefit of this compared + * to ArrayList or LinkedList is that it supports both decent random access and quickly adding to or removing from the + * middle of the list. Operations have the following running times: + * + * size(): O(1) + * get, set, add, remove: O(log N) + * addAll: O(log N + P), where P is the number of elements we add + * iterator(): O(log N + P + M log N), where P is the number of elements over which we iterate and M is the number of + * elements we remove + * listIterator: O(log N + P + M log N + R log N), where P is the number of times we iterate over or set an element, M + * is the number of elements we add or remove, and R is the number of times we change the direction of iteration + * clear(): O(1), excluding garbage collection + * subList.clear(): O(log N), excluding garbage collection + * Constructor: O(N) + * + * This class is similar to an Apache Commons Collections class by the same name. I speculate that the Apache class is + * faster than this (by a constant factor) for most operations. However, this class's implementations of addAll and + * subList.clear() are asymptotically faster than the Apache class's implementations. + */ +public class TreeList extends AbstractList { + /** The dummy leaf node. */ + private final TreeListNode leaf = new TreeListNode(null); + + /** The root node of the tree. */ + private TreeListNode root; + + /** Constructs a new empty TreeList. */ + public TreeList() { + root = leaf; + } + + /** Constructs a new TreeList containing the specified values, in iteration order. */ + public TreeList(Collection values) { + root = createTree(values); + } + + /** Returns the root of a perfectly height-balanced tree containing the specified values, in iteration order. */ + private TreeListNode createTree(Collection values) { + List> nodes = new ArrayList>(values.size()); + for (T value : values) { + nodes.add(new TreeListNode(value)); + } + return RedBlackNode.>createTree(nodes, leaf); + } + + /** + * Returns the node for get(index). Raises an IndexOutOfBoundsException if "index" is not in the range [0, size()). + */ + private TreeListNode getNode(int index) { + if (index < 0 || index >= root.size) { + throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + ")"); + } + int rank = index; + TreeListNode node = root; + while (rank != node.left.size) { + if (rank < node.left.size) { + node = node.left; + } else { + rank -= node.left.size + 1; + node = node.right; + } + } + return node; + } + + @Override + public T get(int index) { + return getNode(index).value; + } + + @Override + public int size() { + return root.size; + } + + @Override + public boolean isEmpty() { + return root == leaf; + } + + @Override + public T set(int index, T value) { + modCount++; + TreeListNode node = getNode(index); + T oldValue = node.value; + node.value = value; + return oldValue; + } + + @Override + public void add(int index, T value) { + if (index < 0 || index > root.size) { + throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); + } + modCount++; + + TreeListNode newNode = new TreeListNode(value); + newNode.left = leaf; + newNode.right = leaf; + if (root.isLeaf()) { + root = newNode; + newNode.isRed = false; + return; + } + newNode.isRed = true; + if (index < root.size) { + TreeListNode node = getNode(index); + if (node.left.isLeaf()) { + node.left = newNode; + newNode.parent = node; + } else { + node = node.predecessor(); + node.right = newNode; + newNode.parent = node; + } + } else { + TreeListNode node; + node = root.max(); + node.right = newNode; + newNode.parent = node; + } + + newNode.fixInsertion(); + while (root.parent != null) { + root = root.parent; + } + } + + @Override + public T remove(int index) { + TreeListNode node = getNode(index); + modCount++; + T value = node.value; + root = node.remove(); + return value; + } + + @Override + public boolean addAll(int index, Collection values) { + if (index < 0 || index > root.size) { + throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); + } + modCount++; + if (values.isEmpty()) { + return false; + } else { + if (index >= root.size) { + root = root.concatenate(createTree(values)); + } else { + TreeListNode[] split = root.split(getNode(index)); + root = split[0].concatenate(createTree(values)).concatenate(split[1]); + } + return true; + } + } + + @Override + public boolean addAll(Collection values) { + modCount++; + if (values.isEmpty()) { + return false; + } else { + root = root.concatenate(createTree(values)); + return true; + } + } + + @Override + protected void removeRange(int startIndex, int endIndex) { + if (startIndex != endIndex) { + modCount++; + TreeListNode last; + if (endIndex == root.size) { + last = leaf; + } else { + TreeListNode[] split = root.split(getNode(endIndex)); + root = split[0]; + last = split[1]; + } + TreeListNode first = root.split(getNode(startIndex))[0]; + root = first.concatenate(last); + } + } + + @Override + public void clear() { + modCount++; + root = leaf; + } + + /** The class for TreeList.iterator(). */ + private class TreeListIterator implements Iterator { + /** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */ + private int modCount = TreeList.this.modCount; + + /** + * The node containing the last element next() returned. This is null if we have yet to call next() or we have + * called remove() since the last call to next(). + */ + private TreeListNode node; + + /** The node containing next(). This is null if we have reached the end of the list. */ + private TreeListNode nextNode; + + /** Whether we have (successfully) called next(). */ + private boolean haveCalledNext; + + private TreeListIterator() { + if (root.isLeaf()) { + nextNode = null; + } else { + nextNode = root.min(); + } + } + + @Override + public boolean hasNext() { + return nextNode != null; + } + + @Override + public T next() { + if (nextNode == null) { + throw new NoSuchElementException("Reached the end of the list"); + } else if (TreeList.this.modCount != modCount) { + throw new ConcurrentModificationException(); + } + haveCalledNext = true; + node = nextNode; + nextNode = nextNode.successor(); + return node.value; + } + + @Override + public void remove() { + if (node == null) { + if (!haveCalledNext) { + throw new IllegalStateException("Must call next() before calling remove()"); + } else { + throw new IllegalStateException("Already removed this element"); + } + } else if (TreeList.this.modCount != modCount) { + throw new ConcurrentModificationException(); + } + root = node.remove(); + node = null; + TreeList.this.modCount++; + modCount = TreeList.this.modCount; + } + } + + @Override + public Iterator iterator() { + return new TreeListIterator(); + } + + /** The class for TreeList.listIterator. */ + private class TreeListListIterator implements ListIterator { + /** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */ + private int modCount = TreeList.this.modCount; + + /** The current return value for nextIndex(). */ + private int nextIndex; + + /** The node for next(), or null if hasNext() is false. */ + private TreeListNode nextNode; + + /** The node for previous(), or null if hasPrevious() is false. */ + private TreeListNode prevNode; + + /** Whether we have called next() or previous(). */ + private boolean haveCalledNextOrPrevious; + + /** Whether we (successfully) called next() more recently than previous(). */ + private boolean justCalledNext; + + /** + * Whether we have (successfully) called remove() or "add" since the last (successful) call to next() or + * previous(). + */ + private boolean haveModified; + + /** + * Constructs a new TreeListListIterator. + * @param index The starting index, as in the "index" argument to listIterator. This method assumes that + * 0 <= index < root.size. + */ + private TreeListListIterator(int index) { + nextIndex = index; + if (index > 0) { + prevNode = getNode(index - 1); + nextNode = prevNode.successor(); + } else { + prevNode = null; + if (root.size > 0) { + nextNode = root.min(); + } else { + nextNode = null; + } + } + } + + @Override + public boolean hasNext() { + if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + return nextNode != null; + } + + @Override + public T next() { + if (nextNode == null) { + throw new NoSuchElementException("Reached the end of the list"); + } else if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + haveCalledNextOrPrevious = true; + justCalledNext = true; + haveModified = false; + nextIndex++; + prevNode = nextNode; + nextNode = nextNode.successor(); + return prevNode.value; + } + + @Override + public int nextIndex() { + if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + return nextIndex; + } + + @Override + public boolean hasPrevious() { + if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + return prevNode != null; + } + + @Override + public T previous() { + if (prevNode == null) { + throw new NoSuchElementException("Reached the beginning of the list"); + } else if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + haveCalledNextOrPrevious = true; + justCalledNext = false; + haveModified = false; + nextIndex--; + nextNode = prevNode; + prevNode = prevNode.predecessor(); + return nextNode.value; + } + + @Override + public int previousIndex() { + return nextIndex - 1; + } + + @Override + public void set(T value) { + if (!haveCalledNextOrPrevious) { + throw new IllegalStateException("Must call next() or previous() before calling \"set\""); + } else if (haveModified) { + throw new IllegalStateException("Already modified the list at this position"); + } else if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + if (justCalledNext) { + prevNode.value = value; + } else { + nextNode.value = value; + } + TreeList.this.modCount++; + modCount = TreeList.this.modCount; + } + + @Override + public void add(T value) { + if (haveModified) { + throw new IllegalStateException("Already modified the list at this position"); + } else if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + + // Create the new node + TreeListNode newNode = new TreeListNode(value);; + newNode.left = leaf; + newNode.right = leaf; + newNode.isRed = true; + + // Insert newNode. There is guaranteed to be a leaf child of prevNode or nextNode where we can insert it. + if (nextNode != null && nextNode.left.isLeaf()) { + nextNode.left = newNode; + newNode.parent = nextNode; + } else if (prevNode != null) { + prevNode.right = newNode; + newNode.parent = prevNode; + } else { + root = newNode; + } + + prevNode = newNode; + newNode.fixInsertion(); + nextIndex++; + haveModified = true; + TreeList.this.modCount++; + modCount = TreeList.this.modCount; + } + + @Override + public void remove() { + if (!haveCalledNextOrPrevious) { + throw new IllegalStateException("Must call next() or previous() before calling remove()"); + } else if (haveModified) { + throw new IllegalStateException("Already modified the list at this position"); + } else if (modCount != TreeList.this.modCount) { + throw new ConcurrentModificationException(); + } + + if (justCalledNext) { + TreeListNode predecessor = prevNode.predecessor(); + root = prevNode.remove(); + prevNode = predecessor; + } else { + TreeListNode successor = nextNode.successor(); + root = nextNode.remove(); + nextNode = successor; + } + + haveModified = true; + TreeList.this.modCount++; + modCount = TreeList.this.modCount; + } + } + + @Override + public ListIterator listIterator(int index) { + if (index < 0 || index > root.size) { + throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); + } + return new TreeListListIterator(index); + } +} diff --git a/src/com/github/btrekkie/tree_list/TreeListNode.java b/src/com/github/btrekkie/tree_list/TreeListNode.java new file mode 100644 index 000000000..a16637e81 --- /dev/null +++ b/src/com/github/btrekkie/tree_list/TreeListNode.java @@ -0,0 +1,40 @@ +package com.github.btrekkie.tree_list; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** A node in a TreeList. See the comments for TreeList. */ +class TreeListNode extends RedBlackNode> { + /** The element stored in the node. The value is unspecified if this is a leaf node. */ + public T value; + + /** The number of elements in the subtree rooted at this node, not counting leaf nodes, as in TreeList.leaf. */ + public int size; + + public TreeListNode(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; + } + } + + @Override + public void assertNodeIsValid() { + int expectedSize; + if (isLeaf()) { + expectedSize = 0; + } else { + expectedSize = left.size + right.size + 1; + } + if (size != expectedSize) { + throw new RuntimeException("The node's size does not match that of the children"); + } + } +} diff --git a/src/com/github/btrekkie/tree_list/test/TreeListTest.java b/src/com/github/btrekkie/tree_list/test/TreeListTest.java new file mode 100644 index 000000000..f59a86f0b --- /dev/null +++ b/src/com/github/btrekkie/tree_list/test/TreeListTest.java @@ -0,0 +1,551 @@ +package com.github.btrekkie.tree_list.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import org.junit.Test; + +import com.github.btrekkie.tree_list.TreeList; + +public class TreeListTest { + /** Tests TreeList.add. */ + @Test + public void testAdd() { + List list = new TreeList(); + assertEquals(Collections.emptyList(), list); + assertEquals(0, list.size()); + assertTrue(list.isEmpty()); + + list.add(4); + list.add(17); + list.add(-4); + list.add(null); + assertEquals(Arrays.asList(4, 17, -4, null), list); + assertEquals(4, list.size()); + assertFalse(list.isEmpty()); + assertEquals(-4, list.get(2).intValue()); + + boolean threwException; + try { + list.get(-3); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.get(9); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.add(-3, 5); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.add(9, 10); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + + list.add(1, 6); + list.add(0, -1); + list.add(6, 42); + assertEquals(Arrays.asList(-1, 4, 6, 17, -4, null, 42), list); + assertEquals(7, list.size()); + assertEquals(6, list.get(2).intValue()); + assertEquals(null, list.get(5)); + + list = new TreeList(); + for (int i = 0; i < 200; i++) { + list.add(i + 300); + } + for (int i = 0; i < 300; i++) { + list.add(i, i); + } + for (int i = 499; i >= 0; i--) { + list.add(500, i + 500); + } + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(777, list.get(777).intValue()); + assertEquals(123, list.get(123).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + } + + /** Tests TreeList.remove. */ + @Test + public void testRemove() { + List list = new TreeList(); + list.add(17); + list.add(-5); + list.add(null); + list.add(0); + list.add(1, 16); + assertEquals(null, list.remove(3)); + assertEquals(17, list.remove(0).intValue()); + assertEquals(0, list.remove(2).intValue()); + assertEquals(Arrays.asList(16, -5), list); + assertEquals(2, list.size()); + assertFalse(list.isEmpty()); + assertEquals(16, list.get(0).intValue()); + assertEquals(-5, list.get(1).intValue()); + + boolean threwException; + try { + list.remove(-3); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.remove(9); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + + list = new TreeList(); + for (int i = 0; i < 1000; i++) { + list.add(i); + } + for (int i = 0; i < 250; i++) { + assertEquals(2 * i + 501, list.remove(501).intValue()); + assertEquals(2 * i + 1, list.remove(i + 1).intValue()); + } + List expected = new ArrayList(500); + for (int i = 0; i < 500; i++) { + expected.add(2 * i); + } + assertEquals(expected, list); + assertEquals(500, list.size()); + assertFalse(list.isEmpty()); + assertEquals(0, list.get(0).intValue()); + assertEquals(998, list.get(499).intValue()); + assertEquals(84, list.get(42).intValue()); + assertEquals(602, list.get(301).intValue()); + } + + /** Tests TreeList.set.*/ + @Test + public void testSet() { + List list = new TreeList(); + list.addAll(Arrays.asList(5, 17, 42, -6, null, 3, null)); + list.set(3, 12); + list.set(0, 6); + list.set(6, 88); + boolean threwException; + try { + list.set(7, 2); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.set(-1, 4); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + assertEquals(Arrays.asList(6, 17, 42, 12, null, 3, 88), list); + assertEquals(7, list.size()); + assertFalse(list.isEmpty()); + assertEquals(42, list.get(2).intValue()); + assertEquals(6, list.get(0).intValue()); + assertEquals(88, list.get(6).intValue()); + + list = new TreeList(); + for (int i = 0; i < 1000; i++) { + list.add(999 - i); + } + for (int i = 0; i < 500; i++) { + list.set(i, i); + list.set(i + 500, i + 500); + } + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(123, list.get(123).intValue()); + assertEquals(777, list.get(777).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + } + + /** Tests TreeList.addAll. */ + @Test + public void testAddAll() { + List list = new TreeList(); + list.add(3); + list.add(42); + list.add(16); + list.addAll(0, Arrays.asList(15, 4, -1)); + list.addAll(2, Arrays.asList(6, 14)); + list.addAll(1, Collections.emptyList()); + list.addAll(8, Arrays.asList(null, 7)); + list.addAll(Arrays.asList(-5, 5)); + assertEquals(Arrays.asList(15, 4, 6, 14, -1, 3, 42, 16, null, 7, -5, 5), list); + assertEquals(12, list.size()); + assertFalse(list.isEmpty()); + assertEquals(14, list.get(3).intValue()); + assertEquals(15, list.get(0).intValue()); + assertEquals(5, list.get(11).intValue()); + + list = new TreeList(); + List list2 = new ArrayList(400); + for (int i = 0; i < 200; i++) { + list2.add(i + 100); + } + for (int i = 0; i < 200; i++) { + list2.add(i + 700); + } + list.addAll(list2); + list2.clear(); + for (int i = 0; i < 100; i++) { + list2.add(i); + } + list.addAll(0, list2); + list2.clear(); + for (int i = 0; i < 400; i++) { + list2.add(i + 300); + } + list.addAll(300, list2); + list2.clear(); + for (int i = 0; i < 100; i++) { + list2.add(i + 900); + } + list.addAll(900, list2); + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(123, list.get(123).intValue()); + assertEquals(777, list.get(777).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + } + + /** Tests TreeList.subList.clear(). */ + @Test + public void testClearSubList() { + List list = new TreeList(); + list.addAll(Arrays.asList(6, 42, -3, 15, 7, 99, 6, 12)); + list.subList(2, 4).clear(); + list.subList(0, 1).clear(); + list.subList(4, 4).clear(); + list.subList(3, 5).clear(); + assertEquals(Arrays.asList(42, 7, 99), list); + assertEquals(3, list.size()); + assertFalse(list.isEmpty()); + assertEquals(42, list.get(0).intValue()); + assertEquals(7, list.get(1).intValue()); + assertEquals(99, list.get(2).intValue()); + + list = new TreeList(); + for (int i = 0; i < 200; i++) { + list.add(-1); + } + for (int i = 0; i < 500; i++) { + list.add(i); + } + for (int i = 0; i < 400; i++) { + list.add(-1); + } + for (int i = 0; i < 500; i++) { + list.add(i + 500); + } + for (int i = 0; i < 600; i++) { + list.add(-1); + } + list.subList(1600, 2200).clear(); + list.subList(777, 777).clear(); + list.subList(700, 1100).clear(); + list.subList(0, 200).clear(); + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(123, list.get(123).intValue()); + assertEquals(777, list.get(777).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + + list = new TreeList(); + list.addAll(Arrays.asList(-3, null, 8, 14, 9, 42)); + list.subList(0, 6).clear(); + assertEquals(Collections.emptyList(), list); + assertEquals(0, list.size()); + assertTrue(list.isEmpty()); + } + + /** Tests TreeList(Collection). */ + @Test + public void testConstructor() { + List list = new TreeList(Arrays.asList(1, 5, 42, -6, null, 3)); + assertEquals(Arrays.asList(1, 5, 42, -6, null, 3), list); + assertEquals(6, list.size()); + assertFalse(list.isEmpty()); + assertEquals(42, list.get(2).intValue()); + assertEquals(1, list.get(0).intValue()); + assertEquals(3, list.get(5).intValue()); + + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + list = new TreeList(expected); + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(123, list.get(123).intValue()); + assertEquals(777, list.get(777).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + } + + /** Tests TreeList.clear(). */ + @Test + public void testClear() { + List list = new TreeList(); + list.addAll(Arrays.asList(7, 16, 5, 42)); + list.clear(); + assertEquals(Collections.emptyList(), list); + assertEquals(0, list.size()); + assertTrue(list.isEmpty()); + + for (int i = 0; i < 1000; i++) { + list.add(i); + } + list.clear(); + assertEquals(Collections.emptyList(), list); + assertEquals(0, list.size()); + assertTrue(list.isEmpty()); + } + + /** Tests TreeList.iterator(). */ + @Test + public void testIterator() { + List list = new TreeList(); + Iterator iterator = list.iterator(); + assertFalse(iterator.hasNext()); + boolean threwException; + try { + iterator.next(); + threwException = false; + } catch (NoSuchElementException exception) { + threwException = true; + } + assertTrue(threwException); + + list = new TreeList(Arrays.asList(42, 16, null, 7, 8, 3, 12)); + iterator = list.iterator(); + try { + iterator.remove(); + threwException = false; + } catch (IllegalStateException exception) { + threwException = true; + } + assertTrue(threwException); + assertTrue(iterator.hasNext()); + assertEquals(42, iterator.next().intValue()); + assertEquals(16, iterator.next().intValue()); + assertEquals(null, iterator.next()); + assertTrue(iterator.hasNext()); + assertEquals(7, iterator.next().intValue()); + iterator.remove(); + try { + iterator.remove(); + threwException = false; + } catch (IllegalStateException exception) { + threwException = true; + } + assertTrue(threwException); + assertTrue(iterator.hasNext()); + assertTrue(iterator.hasNext()); + assertEquals(8, iterator.next().intValue()); + assertTrue(iterator.hasNext()); + assertEquals(3, iterator.next().intValue()); + assertTrue(iterator.hasNext()); + assertEquals(12, iterator.next().intValue()); + assertFalse(iterator.hasNext()); + iterator.remove(); + assertFalse(iterator.hasNext()); + try { + iterator.next(); + threwException = false; + } catch (NoSuchElementException exception) { + threwException = true; + } + assertTrue(threwException); + assertEquals(Arrays.asList(42, 16, null, 8, 3), list); + + list = new TreeList(); + for (int i = 0; i < 1000; i++) { + list.add(i); + } + iterator = list.iterator(); + for (int i = 0; i < 500; i++) { + assertTrue(iterator.hasNext()); + assertEquals(2 * i, iterator.next().intValue()); + assertTrue(iterator.hasNext()); + assertEquals(2 * i + 1, iterator.next().intValue()); + iterator.remove(); + } + assertFalse(iterator.hasNext()); + List expected = new ArrayList(500); + for (int i = 0; i < 500; i++) { + expected.add(2 * i); + } + assertEquals(expected, list); + } + + /** Tests TreeList.listIterator. */ + @Test + public void testListIterator() { + List list = new TreeList(); + list.addAll(Arrays.asList(7, 16, 42, -3, 12, 25, 8, 9)); + boolean threwException; + try { + list.listIterator(-1); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + try { + list.listIterator(18); + threwException = false; + } catch (IndexOutOfBoundsException exception) { + threwException = true; + } + assertTrue(threwException); + ListIterator iterator = list.listIterator(1); + assertTrue(iterator.hasNext()); + assertTrue(iterator.hasPrevious()); + assertEquals(16, iterator.next().intValue()); + iterator.add(6); + try { + iterator.set(-1); + threwException = false; + } catch (IllegalStateException exception) { + threwException = true; + } + assertTrue(threwException); + try { + iterator.add(9); + threwException = false; + } catch (IllegalStateException exception) { + threwException = true; + } + assertTrue(threwException); + try { + iterator.remove(); + threwException = false; + } catch (IllegalStateException exception) { + threwException = true; + } + assertTrue(threwException); + assertEquals(6, iterator.previous().intValue()); + assertEquals(6, iterator.next().intValue()); + assertEquals(2, iterator.previousIndex()); + assertEquals(3, iterator.nextIndex()); + assertEquals(42, iterator.next().intValue()); + assertEquals(-3, iterator.next().intValue()); + assertEquals(12, iterator.next().intValue()); + iterator.remove(); + assertEquals(-3, iterator.previous().intValue()); + iterator.remove(); + assertEquals(25, iterator.next().intValue()); + iterator.set(14); + assertEquals(8, iterator.next().intValue()); + assertEquals(9, iterator.next().intValue()); + assertFalse(iterator.hasNext()); + try { + iterator.next(); + threwException = false; + } catch (NoSuchElementException exception) { + threwException = true; + } + assertTrue(threwException); + assertEquals(9, iterator.previous().intValue()); + iterator.set(10); + assertEquals(Arrays.asList(7, 16, 6, 42, 14, 8, 10), list); + assertEquals(7, list.size()); + assertFalse(list.isEmpty()); + assertEquals(42, list.get(3).intValue()); + assertEquals(7, list.get(0).intValue()); + assertEquals(10, list.get(6).intValue()); + + list = new TreeList(); + for (int i = 0; i < 1000; i++) { + list.add(-1); + } + iterator = list.listIterator(); + for (int i = 0; i < 500; i++) { + iterator.add(i); + iterator.next(); + } + for (int i = 0; i < 500; i++) { + iterator.previous(); + iterator.remove(); + iterator.previous(); + } + iterator = list.listIterator(500); + for (int i = 0; i < 250; i++) { + iterator.next(); + iterator.set(2 * i + 500); + iterator.next(); + } + for (int i = 0; i < 250; i++) { + iterator.previous(); + iterator.set(999 - 2 * i); + iterator.previous(); + } + List expected = new ArrayList(1000); + for (int i = 0; i < 1000; i++) { + expected.add(i); + } + assertEquals(expected, list); + assertEquals(1000, list.size()); + assertFalse(list.isEmpty()); + assertEquals(123, list.get(123).intValue()); + assertEquals(777, list.get(777).intValue()); + assertEquals(0, list.get(0).intValue()); + assertEquals(999, list.get(999).intValue()); + } +} From 754e11ade7c5c6e96d7dfe5278d2c5731f730e50 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Mon, 23 May 2016 13:57:28 -0700 Subject: [PATCH 03/24] Escaped "<" and ">" This escapes the "<" and ">" characters in README.md. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0883fb752..4d6f181da 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ augmented) # Example
 /** Red-black tree augmented by the sum of the values in the subtree. */
-public class SumNode extends RedBlackNode {
+public class SumNode extends RedBlackNode<SumNode> {
     public int value;
     public int sum;
 

From f7f14ff852b0cb9acac6e7e96381a29e2b0d12a9 Mon Sep 17 00:00:00 2001
From: Bill Jacobs 
Date: Mon, 23 May 2016 14:00:40 -0700
Subject: [PATCH 04/24] Added RedBlackNode.jar

This adds RedBlackNode.jar for using RedBlackNode in binary form.
---
 .gitignore       |   1 -
 RedBlackNode.jar | Bin 0 -> 36972 bytes
 2 files changed, 1 deletion(-)
 create mode 100644 RedBlackNode.jar

diff --git a/.gitignore b/.gitignore
index 32858aad3..d33629a3f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,7 +4,6 @@
 .mtj.tmp/
 
 # Package Files #
-*.jar
 *.war
 *.ear
 
diff --git a/RedBlackNode.jar b/RedBlackNode.jar
new file mode 100644
index 0000000000000000000000000000000000000000..5351b7e7ce68973af6005adcc31aed919255359e
GIT binary patch
literal 36972
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

*w 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 literal 0 HcmV?d00001 From 82c1fbdc7bd197dcabcf0262d1d7c7137c3c0d95 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Thu, 26 May 2016 10:37:25 -0700 Subject: [PATCH 05/24] Made fixInsertion return the new root This changes RedBlackNode.fixInsertion to return the root of the resulting tree. --- README.md | 2 +- RedBlackNode.jar | Bin 36972 -> 39254 bytes .../btrekkie/red_black_node/RedBlackNode.java | 51 +++++++++++++----- .../github/btrekkie/tree_list/TreeList.java | 7 +-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 4d6f181da..b0d001921 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ augmented) size. * Tested in Java 6.0 and 7.0. It might also work in Java 5.0. -# Limitations: +# Limitations * 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 diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 5351b7e7ce68973af6005adcc31aed919255359e..e6a079f0450a28835a8963953b2d53cb705f57d6 100644 GIT binary patch delta 18303 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 EKLTDdKL7v# diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 8393fafeb..1089cf214 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -232,10 +232,11 @@ public abstract class RedBlackNode> implements Compara * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). + * rotateRight(). This method is more efficient than fixInsertion if "augment" is false or augment() might return + * false. * @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment(). */ - public void fixInsertion(boolean augment) { + public void fixInsertionWithoutGettingRoot(boolean augment) { if (!isRed) { throw new IllegalArgumentException("The node must be red"); } @@ -307,10 +308,35 @@ public abstract class RedBlackNode> implements Compara * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). + * rotateRight(). This method is more efficient than fixInsertion() if augment() might return false. */ - public void fixInsertion() { - fixInsertion(true); + public void fixInsertionWithoutGettingRoot() { + fixInsertionWithoutGettingRoot(true); + } + + /** + * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements + * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be + * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and + * rotateRight(). + * @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment(). + * @return The root of the resulting tree. + */ + public N fixInsertion(boolean augment) { + fixInsertionWithoutGettingRoot(augment); + return root(); + } + + /** + * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements + * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be + * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and + * rotateRight(). + * @return The root of the resulting tree. + */ + public N fixInsertion() { + fixInsertionWithoutGettingRoot(true); + return root(); } /** Returns a Comparator that compares instances of N using their natural order, as in N.compare. */ @@ -383,8 +409,7 @@ public abstract class RedBlackNode> implements Compara } } newNode.isRed = true; - newNode.fixInsertion(); - return root(); + return newNode.fixInsertion(); } /** @@ -757,9 +782,7 @@ public abstract class RedBlackNode> implements Compara } // Perform insertion fixup - pivot.fixInsertion(); - - return pivot.root(); + return pivot.fixInsertion(); } /** @@ -882,7 +905,7 @@ public abstract class RedBlackNode> implements Compara } last = lastPivot.left; lastParent = lastPivot; - lastPivot.fixInsertion(false); + lastPivot.fixInsertionWithoutGettingRoot(false); } lastPivot = node; node = node.left; @@ -925,7 +948,7 @@ public abstract class RedBlackNode> implements Compara } first = firstPivot.right; firstParent = firstPivot; - firstPivot.fixInsertion(false); + firstPivot.fixInsertionWithoutGettingRoot(false); } firstPivot = node; node = node.right; @@ -964,7 +987,7 @@ public abstract class RedBlackNode> implements Compara } firstPivot.left = leaf; firstPivot.right = leaf; - firstPivot.fixInsertion(false); + firstPivot.fixInsertionWithoutGettingRoot(false); for (first = firstPivot; first.parent != null; first = first.parent) { first.augment(); } @@ -982,7 +1005,7 @@ public abstract class RedBlackNode> implements Compara } lastPivot.left = leaf; lastPivot.right = leaf; - lastPivot.fixInsertion(false); + lastPivot.fixInsertionWithoutGettingRoot(false); for (last = lastPivot; last.parent != null; last = last.parent) { last.augment(); } diff --git a/src/com/github/btrekkie/tree_list/TreeList.java b/src/com/github/btrekkie/tree_list/TreeList.java index b5de0ccbc..dea9bb65c 100644 --- a/src/com/github/btrekkie/tree_list/TreeList.java +++ b/src/com/github/btrekkie/tree_list/TreeList.java @@ -133,10 +133,7 @@ public class TreeList extends AbstractList { newNode.parent = node; } - newNode.fixInsertion(); - while (root.parent != null) { - root = root.parent; - } + root = newNode.fixInsertion(); } @Override @@ -418,7 +415,7 @@ public class TreeList extends AbstractList { } prevNode = newNode; - newNode.fixInsertion(); + root = newNode.fixInsertion(); nextIndex++; haveModified = true; TreeList.this.modCount++; From f355e1ed2b247d882b5f302dd967af7c50c31424 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Thu, 26 May 2016 12:22:32 -0700 Subject: [PATCH 06/24] Added LCA This adds a RedBlackNode method for computing the lowest common ancestor of two nodes. --- .gitignore | 2 + README.md | 4 +- .../btrekkie/red_black_node/RedBlackNode.java | 103 ++++++++++++++---- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index d33629a3f..610859901 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* + +bin/.gitignore diff --git a/README.md b/README.md index b0d001921..5afa37bdd 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,8 @@ augmented) # Features * Supports min, max, root, predecessor, successor, insert, remove, rotate, - split, concatenate, create balanced tree, and compare operations. The running - time of each operation has optimal big O bounds. + 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. diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 1089cf214..ebc8d6172 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -169,13 +169,13 @@ public abstract class RedBlackNode> implements Compara } /** - * Performs a left rotation about this node. This method assumes that !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. * @return The return value from calling augment() on the resulting parent. */ public boolean rotateLeft() { - if (right.isLeaf()) { - throw new IllegalArgumentException("The right child is a leaf"); + if (isLeaf() || right.isLeaf()) { + throw new IllegalArgumentException("The node or its right child is a leaf"); } N newParent = right; right = newParent.left; @@ -199,13 +199,13 @@ public abstract class RedBlackNode> implements Compara } /** - * Performs a right rotation about this node. This method assumes that !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. * @return The return value from calling augment() on the resulting parent. */ public boolean rotateRight() { - if (left.isLeaf()) { - throw new IllegalArgumentException("The left child is a leaf"); + if (isLeaf() || left.isLeaf()) { + throw new IllegalArgumentException("The node or its left child is a leaf"); } N newParent = left; left = newParent.right; @@ -231,9 +231,9 @@ public abstract class RedBlackNode> implements Compara /** * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be - * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). This method is more efficient than fixInsertion if "augment" is false or augment() might return - * false. + * red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs + * any rotations by calling rotateLeft() and rotateRight(). This method is more efficient than fixInsertion if + * "augment" is false or augment() might return false. * @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment(). */ public void fixInsertionWithoutGettingRoot(boolean augment) { @@ -307,8 +307,9 @@ public abstract class RedBlackNode> implements Compara /** * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be - * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). This method is more efficient than fixInsertion() if augment() might return false. + * red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs + * any rotations by calling rotateLeft() and rotateRight(). This method is more efficient than fixInsertion() if + * augment() might return false. */ public void fixInsertionWithoutGettingRoot() { fixInsertionWithoutGettingRoot(true); @@ -317,8 +318,8 @@ public abstract class RedBlackNode> implements Compara /** * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be - * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). + * red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs + * any rotations by calling rotateLeft() and rotateRight(). * @param augment Whether to set the augmentation information for "node" and its ancestors, by calling augment(). * @return The root of the resulting tree. */ @@ -330,8 +331,8 @@ public abstract class RedBlackNode> implements Compara /** * Performs red-black insertion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that this may be a red child of a red node, and if this is the root, the root may be - * red. node.isRed must initially be true. The method performs any rotations by calling rotateLeft() and - * rotateRight(). + * red. node.isRed must initially be true. This method assumes that this is not a leaf node. The method performs + * any rotations by calling rotateLeft() and rotateRight(). * @return The root of the resulting tree. */ public N fixInsertion() { @@ -414,7 +415,7 @@ public abstract class RedBlackNode> implements Compara /** * Moves this node to its successor's former position in the tree and vice versa, i.e. sets the "left", "right", - * "parent", and isRed fields of each. + * "parent", and isRed fields of each. This method assumes that this is not a leaf node. * @return The node with which we swapped. */ private N swapWithSuccessor() { @@ -462,7 +463,7 @@ public abstract class RedBlackNode> implements Compara /** * Performs red-black deletion fixup. To be more precise, this fixes a tree that satisfies all of the requirements * of red-black trees, except that all paths from the root to a leaf that pass through the sibling of this node have - * one fewer black node than all other root-to-leaf paths. + * one fewer black node than all other root-to-leaf paths. This method assumes that this is not a leaf node. */ private void fixSiblingDeletion() { RedBlackNode sibling = this; @@ -531,7 +532,8 @@ public abstract class RedBlackNode> implements Compara /** * Removes this node from the tree that contains it. The effect of this method on the fields of this node is - * unspecified. This method is more efficient than remove() if augment() might return false. + * unspecified. This method assumes that this is not a leaf node. This method is more efficient than remove() if + * augment() might return false. * * If the node has two children, we begin by moving the node's successor to its former position, by changing its * "left", "right", "parent", and "isBlack" fields. @@ -609,7 +611,7 @@ public abstract class RedBlackNode> implements Compara /** * Removes this node from the tree that contains it. The effect of this method on the fields of this node is - * unspecified. + * unspecified. This method assumes that this is not a leaf node. * * If the node has two children, we begin by moving the node's successor to its former position, by changing its * "left", "right", "parent", and "isBlack" fields. @@ -812,8 +814,10 @@ public abstract class RedBlackNode> implements Compara * Splits the tree rooted at this node into two trees, so that the first element of the return value is the root of * a tree consisting of the nodes that were before the specified node, and the second element of the return value is * the root of a tree consisting of the nodes that were equal to or after the specified node. This method assumes - * that this node is the root. It takes O(log N) time. It is considerably more efficient than removing all of the - * elements after splitNode and then creating a new tree from those nodes. + * that this node is the root. It assumes that this is in the same tree as splitNode. It takes O(log N) time. It + * is considerably more efficient than removing all of the elements after splitNode and then creating a new tree + * from those nodes. + * @param The node at which to split the tree. * @return An array consisting of the resulting trees. */ public N[] split(N splitNode) { @@ -1019,15 +1023,66 @@ public abstract class RedBlackNode> implements Compara return result; } + /** + * 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". + */ + public N lca(N other) { + if (isLeaf() || other.isLeaf()) { + throw new IllegalArgumentException("One of the nodes is a leaf node"); + } + + // Compute the depth of each node + int depth = 0; + for (N parent = this.parent; parent != null; parent = parent.parent) { + depth++; + } + int otherDepth = 0; + for (N parent = other.parent; parent != null; parent = parent.parent) { + otherDepth++; + } + + // Go up to nodes of the same depth + @SuppressWarnings("unchecked") + N parent = (N)this; + N otherParent = other; + if (depth <= otherDepth) { + for (int i = otherDepth; i > depth; i--) { + otherParent = otherParent.parent; + } + } else { + for (int i = depth; i > otherDepth; i--) { + parent = parent.parent; + } + } + + // Find the LCA + while (parent != otherParent) { + parent = parent.parent; + otherParent = otherParent.parent; + } + if (parent != null) { + return parent; + } else { + throw new IllegalArgumentException("The nodes do not belong to the same tree"); + } + } + /** * 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". + * 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 * nodes, then it could override compareTo to compare the nodes' values, which would take O(1) time. */ public int compareTo(N other) { + if (isLeaf() || other.isLeaf()) { + throw new IllegalArgumentException("One of the nodes is a leaf node"); + } + // The algorithm operates as follows: compare the depth of this node to that of "other". If the depth of // "other" is greater, keep moving up from "other" until we find the ancestor at the same depth. Then, keep // moving up from "this" and from that node until we reach the lowest common ancestor. The node that arrived From a1dba8a58ca5abe23a781dbf082a6e8cf96ab963 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Thu, 26 May 2016 14:47:27 -0700 Subject: [PATCH 07/24] Added test for "lca" This adds SubArrayMinTest, which tests RedBlackNode.lca. --- README.md | 2 + RedBlackNode.jar | Bin 39254 -> 43396 bytes .../btrekkie/red_black_node/RedBlackNode.java | 15 +++- .../red_black_node/test/RedBlackNodeTest.java | 8 +- .../btrekkie/sub_array_min/SubArrayMin.java | 85 ++++++++++++++++++ .../sub_array_min/SubArrayMinNode.java | 56 ++++++++++++ .../sub_array_min/test/SubArrayMinTest.java | 45 ++++++++++ .../github/btrekkie/tree_list/TreeList.java | 2 +- .../btrekkie/tree_list/TreeListNode.java | 2 +- 9 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 src/com/github/btrekkie/sub_array_min/SubArrayMin.java create mode 100644 src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java create mode 100644 src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java diff --git a/README.md b/README.md index 5afa37bdd..2e27cdde9 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,8 @@ augmented) * Tested in Java 6.0 and 7.0. It might also work in Java 5.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 diff --git a/RedBlackNode.jar b/RedBlackNode.jar index e6a079f0450a28835a8963953b2d53cb705f57d6..fe3488c515c61f8601ebeb71bc5264ed900c5d9c 100644 GIT binary patch delta 15297 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%0D7?rHR^+u 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 implements Compara * "left", "right", "parent", and "isBlack" fields. */ public void removeWithoutGettingRoot() { + if (isLeaf()) { + throw new IllegalArgumentException("Attempted to remove a leaf node"); + } N replacement; if (left.isLeaf() || right.isLeaf()) { replacement = null; @@ -619,6 +624,10 @@ public abstract class RedBlackNode> implements Compara * @return The root of the resulting tree. */ public N remove() { + if (isLeaf()) { + throw new IllegalArgumentException("Attempted to remove a leaf node"); + } + // Find an arbitrary non-leaf node in the tree other than this node N node; if (parent != null) { diff --git a/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java b/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java index 6059745aa..4d571aeb1 100644 --- a/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java +++ b/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java @@ -8,10 +8,10 @@ import java.util.Comparator; import org.junit.Test; /** - * Tests RedBlackNode. Most of the testing for RedBlackNode takes place in TreeListTest, IntervalTreeTest, and - * ArbitraryOrderCollectionTest, which test realistic use cases of RedBlackNode. TreeListTest tests most of the - * RedBlackNode methods, while IntervalTreeTest tests non-structural augmentation and the "insert" method, - * ArbitraryOrderCollectionTest tests compareTo, and RedBlackNodeTest tests assertSubtreeIsValid() and + * Tests RedBlackNode. Most of the testing for RedBlackNode takes place in TreeListTest, IntervalTreeTest, + * SubArrayMinTest, and ArbitraryOrderCollectionTest, which test realistic use cases of RedBlackNode. TreeListTest + * tests most of the RedBlackNode methods, while IntervalTreeTest tests the "insert" method, SubArrayMinTest tests + * "lca", ArbitraryOrderCollectionTest tests compareTo, and RedBlackNodeTest tests assertSubtreeIsValid() and * assertOrderIsValid. */ public class RedBlackNodeTest { diff --git a/src/com/github/btrekkie/sub_array_min/SubArrayMin.java b/src/com/github/btrekkie/sub_array_min/SubArrayMin.java new file mode 100644 index 000000000..874db6575 --- /dev/null +++ b/src/com/github/btrekkie/sub_array_min/SubArrayMin.java @@ -0,0 +1,85 @@ +package com.github.btrekkie.sub_array_min; + +/** A list of integers. SubArrayMin provides the ability to quickly determine the minimum value in a given sublist. */ +/* We implement SubArrayMin using a red-black tree augmented by subtree size and minimum value. Using the subtree size + * augmentation, we can find the node at a given index. + */ +public class SubArrayMin { + /** The root node. */ + private SubArrayMinNode root = SubArrayMinNode.LEAF; + + /** Appends the specified value to the end of the list. */ + public void add(int value) { + SubArrayMinNode newNode = new SubArrayMinNode(value); + newNode.left = SubArrayMinNode.LEAF; + newNode.right = SubArrayMinNode.LEAF; + if (root.isLeaf()) { + root = newNode; + newNode.augment(); + } else { + SubArrayMinNode node = root.max(); + node.right = newNode; + newNode.parent = node; + newNode.isRed = true; + root = newNode.fixInsertion(); + } + } + + /** Returns the node for the element with the specified index. Assumes "index" is in the range [0, root.size). */ + private SubArrayMinNode getNode(int index) { + if (index < 0 || index >= root.size) { + throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + ")"); + } + int rank = index; + SubArrayMinNode node = root; + while (rank != node.left.size) { + if (rank < node.left.size) { + node = node.left; + } else { + rank -= node.left.size + 1; + node = node.right; + } + } + return node; + } + + /** + * Returns the minimum value in the subarray starting at index startIndex and ending at index endIndex - 1, + * inclusive. Assumes startIndex < endIndex, and assumes this contains indices startIndex and endIndex - 1. + */ + public int min(int startIndex, int endIndex) { + if (startIndex >= endIndex) { + throw new IllegalArgumentException("The start index must be less than the end index"); + } + SubArrayMinNode start = getNode(startIndex); + SubArrayMinNode end = getNode(endIndex - 1); + SubArrayMinNode lca = start.lca(end); + + int min = Math.min(lca.value, Math.min(start.value, end.value)); + if (start != lca) { + for (SubArrayMinNode node = start; node.parent != lca; node = node.parent) { + if (node.parent.left == node) { + if (node.parent.value < min) { + min = node.parent.value; + } + if (node.parent.right.min < min) { + min = node.parent.right.min; + } + } + } + } + if (end != lca) { + for (SubArrayMinNode node = end; node.parent != lca; node = node.parent) { + if (node.parent.right == node) { + if (node.parent.value < min) { + min = node.parent.value; + } + if (node.parent.left.min < min) { + min = node.parent.left.min; + } + } + } + } + return min; + } +} diff --git a/src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java b/src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java new file mode 100644 index 000000000..1839b7220 --- /dev/null +++ b/src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java @@ -0,0 +1,56 @@ +package com.github.btrekkie.sub_array_min; + +import com.github.btrekkie.red_black_node.RedBlackNode; + +/** A node in a SubArrayMin object. See the comments for the implementation of that class. */ +class SubArrayMinNode extends RedBlackNode { + /** The dummy leaf node. */ + public static final SubArrayMinNode LEAF = new SubArrayMinNode(); + + /** The element stored in the node. The value is unspecified if this is a leaf node. */ + public final int value; + + /** The number of elements in the subtree rooted at this node. */ + public int size; + + /** The minimum element in the subtree rooted at this node. This is Integer.MAX_VALUE if this is a leaf node. */ + public int min; + + public SubArrayMinNode(int value) { + this.value = value; + } + + private SubArrayMinNode() { + value = 0; + min = Integer.MAX_VALUE; + } + + @Override + public boolean augment() { + int newSize = left.size + right.size + 1; + int newMin = Math.min(value, Math.min(left.min, right.min)); + if (newSize == size && newMin == min) { + return false; + } else { + size = newSize; + min = newMin; + return true; + } + } + + @Override + public void assertNodeIsValid() { + int expectedSize; + int expectedMin; + if (isLeaf()) { + expectedSize = 0; + expectedMin = Integer.MAX_VALUE; + } else { + expectedSize = left.size + right.size + 1; + expectedMin = Math.min(value, Math.min(left.min, right.min)); + } + if (size != expectedSize || min != expectedMin) { + throw new RuntimeException("The node's size or minimum value does not match that of the children"); + } + } +} diff --git a/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java b/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java new file mode 100644 index 000000000..d69657f7a --- /dev/null +++ b/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java @@ -0,0 +1,45 @@ +package com.github.btrekkie.sub_array_min.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.github.btrekkie.sub_array_min.SubArrayMin; + +public class SubArrayMinTest { + /** Tests SubArrayMin. */ + @Test + public void test() { + SubArrayMin sam = new SubArrayMin(); + sam.add(12); + sam.add(42); + sam.add(16); + sam.add(-3); + sam.add(8); + sam.add(5); + sam.add(4); + assertEquals(-3, sam.min(0, 7)); + assertEquals(12, sam.min(0, 3)); + assertEquals(-3, sam.min(2, 4)); + assertEquals(12, sam.min(0, 1)); + assertEquals(5, sam.min(4, 6)); + assertEquals(4, sam.min(4, 7)); + + sam = new SubArrayMin(); + for (int i = 0; i < 1000; i++) { + // Taken from http://stackoverflow.com/a/109025 + int value1 = i - ((i >>> 1) & 0x55555555); + int value2 = (value1 & 0x33333333) + ((value1 >>> 2) & 0x33333333); + int setBitCount = (((value2 + (value2 >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24; + + sam.add(-setBitCount); + } + assertEquals(0, sam.min(0, 1)); + assertEquals(-4, sam.min(0, 30)); + assertEquals(-9, sam.min(0, 1000)); + assertEquals(-9, sam.min(123, 777)); + assertEquals(-8, sam.min(777, 888)); + assertEquals(-6, sam.min(777, 788)); + assertEquals(-9, sam.min(900, 1000)); + } +} diff --git a/src/com/github/btrekkie/tree_list/TreeList.java b/src/com/github/btrekkie/tree_list/TreeList.java index dea9bb65c..ba96c716f 100644 --- a/src/com/github/btrekkie/tree_list/TreeList.java +++ b/src/com/github/btrekkie/tree_list/TreeList.java @@ -57,7 +57,7 @@ public class TreeList extends AbstractList { } /** - * Returns the node for get(index). Raises an IndexOutOfBoundsException if "index" is not in the range [0, size()). + * Returns the node for get(index). Throws an IndexOutOfBoundsException if "index" is not in the range [0, size()). */ private TreeListNode getNode(int index) { if (index < 0 || index >= root.size) { diff --git a/src/com/github/btrekkie/tree_list/TreeListNode.java b/src/com/github/btrekkie/tree_list/TreeListNode.java index a16637e81..de78368db 100644 --- a/src/com/github/btrekkie/tree_list/TreeListNode.java +++ b/src/com/github/btrekkie/tree_list/TreeListNode.java @@ -7,7 +7,7 @@ class TreeListNode extends RedBlackNode> { /** The element stored in the node. The value is unspecified if this is a leaf node. */ public T value; - /** The number of elements in the subtree rooted at this node, not counting leaf nodes, as in TreeList.leaf. */ + /** The number of elements in the subtree rooted at this node. */ public int size; public TreeListNode(T value) { From dd0fd1959c32f557539a8a2b1fb4e2e064cedbd1 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Sat, 28 May 2016 10:56:08 -0700 Subject: [PATCH 08/24] Clear links after removing node This changes the remove methods to set the parent and child links to be null, so that we're more likely to encounter an exception if we attempt to access the node. --- .../btrekkie/red_black_node/RedBlackNode.java | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index cc84a6ce7..26fbaffe7 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -342,7 +342,7 @@ public abstract class RedBlackNode> implements Compara return root(); } - /** Returns a Comparator that compares instances of N using their natural order, as in N.compare. */ + /** Returns a Comparator that compares instances of N using their natural order, as in N.compareTo. */ private Comparator naturalOrder() { @SuppressWarnings("unchecked") Comparator comparator = (Comparator)NATURAL_ORDER; @@ -537,8 +537,8 @@ public abstract class RedBlackNode> implements Compara * unspecified. This method assumes that this is not a leaf node. This method is more efficient than remove() if * augment() might return false. * - * If the node has two children, we begin by moving the node's successor to its former position, by changing its - * "left", "right", "parent", and "isBlack" fields. + * If the node has two children, we begin by moving the node's successor to its former position, by changing the + * successor's "left", "right", "parent", and isRed fields. */ public void removeWithoutGettingRoot() { if (isLeaf()) { @@ -612,14 +612,21 @@ public abstract class RedBlackNode> implements Compara } } } + + // Clear any previously existing links, so that we're more likely to encounter an exception if we attempt to + // access the removed node + parent = null; + left = null; + right = null; + isRed = true; } /** * Removes this node from the tree that contains it. The effect of this method on the fields of this node is * unspecified. This method assumes that this is not a leaf node. * - * If the node has two children, we begin by moving the node's successor to its former position, by changing its - * "left", "right", "parent", and "isBlack" fields. + * If the node has two children, we begin by moving the node's successor to its former position, by changing the + * successor's "left", "right", "parent", and isRed fields. * * @return The root of the resulting tree. */ From 4e1fe67095012aaef289336174f251807c48d533 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Sat, 28 May 2016 17:35:32 -0700 Subject: [PATCH 09/24] Updated RedBlackNode.jar to clear links after removing node This updates RedBlackNode.jar to include the changes in dd0fd1959c32f557539a8a2b1fb4e2e064cedbd1. --- RedBlackNode.jar | Bin 43396 -> 43422 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index fe3488c515c61f8601ebeb71bc5264ed900c5d9c..2cd9a1ecf0d0a533dd961254e369d1b8a08770d4 100644 GIT binary patch delta 9815 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 From deb397ed1ed068cf578bd3b9d3de083b928a6f0f Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Tue, 31 May 2016 23:07:52 -0700 Subject: [PATCH 10/24] Changed fixInsertion to always augment parent This changes fixInsertion to always augment the node's parent, even if the initial call to augment() returns false, assuming "augment" is true. When we insert a node, we are supposed to ignore its initial state; thus, we ignore the return value of augment(). --- RedBlackNode.jar | Bin 43422 -> 43393 bytes .../btrekkie/red_black_node/RedBlackNode.java | 8 ++------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 2cd9a1ecf0d0a533dd961254e369d1b8a08770d4..1cb1d9866eb60a42e3caf5c9481b16f0f2967ffe 100644 GIT binary patch delta 9224 zcmX|{RZyHwu!e&Zg1gJ2!QCaeyDyU9?yesYAh<2C$O6IL-Q6L$1W1rTaCZ{mNDLv$Zl?;Mj zgxZ+P%nbh@Op5wnNDI6TOkKLY^MHQ0X}}$$gTqb7w#LP#)uh#U|5Zl12vI|Fn1#0xgIuGH;Gu{@L$0)VoC6{*=vLeusj9!0~jFU?%W_2d^H~ZFm~sA z4QpqsFM;egweXF~tHlbu!Zt|{h7s73JhU9$94(cryk5o%f9K-6DEV&@9zbexW?Nh} ztHk(Q>DjUEi6w$Ynl5iW60-Q+=6OSVZc9GeaL=V2K^+|=WcpSzGB!z-y;~N(=AE}J zWIX*)O*V8W_W;HOpVa$QDR!zALp5*zv?-BRl?cq1I!DjY!qwaj{YCn#;;A73$EDmS zQc_Mom!*(U*h4UAqqX^VL_El@CkW;|#w3BJK`cZ3Sae5ByNnXLLoIrNX1T%${)LhA zZhvIG)JSi-(#0XBc`B}%@!^NgQ@)0RJslC9@amM9Fbrs6a!#!Ju5S@Xqg*E>R{!ns zUA&lGw`ItNfxLW|*tp4&u=NNS*OJ($pirDLd$a@V9q66p+Up42+bmoo%bD8s^)&@K zui=ZlI+mK4?IMiWS~O7K4xbNg8X*JtSx1aHDxY}jyq^2o7Wp_rh-Ck;|nFE+Nz$NKck(`ymGZAd!rFcCeTQ?+Rlvves( z6!dDh*y*%5+ETu}U%_r6Oh(zmuAq=tZ%r!w;um$?UOasUzK$hzGuQWA+ZwK5?hLrSHp=X|U} zNvY0Yw`^yP;yGNk>K(OLCWbC6@LOU8I4?+nY{#e?)oQv1&V&n&A^CGy!er!}8KX zQ^<0EDGg_@*zy`o*wK%~1+R)-)X^XN%eHIirxI|JMA49XUubqD4z|HXy(GGPI3n35 zjsqZ6(JiF54pw+r`&@X~`=tbb_E}YpfTF+K+lz5Z3o%{qP<7(&xzv& zPaj}J)n1sOZ4pbnA79=$&EtivImm;w_A7SmliOtIUTZV2y0ZSFp{ZD{jZ?SVb9aYY zBfMl@ZgV+Lw)~c}%2aV1Ct%)1?<~2oV77$Ku>R}aI^n2~Sez-0jng%do8)5~6A9qL zuG*3YHaT5zOrYb~he~=<4-{XA8BEL#SYh@=>jWMl+*zjU{z%6^1KhA}+yOv40ERA_ za|hnKi9yd^rvF6}({CKUaW#L;i@}`bMkC1|uLdNQz80 zlOb|~=2aq7th%L}b^Qd+IFU@OEg!nB;DxzETE&ZAkPuLVw$s{{xvGQ(g_-FS9na5Y z^9d7L^!Z$&?Zl6~&<89e2@5ngHw5HsNag^Y=i+~Q8>Hdxezyp98?a~y17-Bf$>dEo zwbP?{{z>hG^n@OcGnr7^a|P0Th?7i*3V|Q^?0ey`3z)$mcxZImyWMXS<3@Y=xOz;m zfh>&J&aVw6tZkOr34KRzLr(eN>%|AOQ6S8;5ePFEFI;tO#7IPw$s{KsV2xSaAZ+Eg zbnYsy_7V?p`u)?JT-R3=h+@281aJQJb%)PP`vlt@?9EUD-&%_v_7jE%AC}xbh$5el zU7nuSFNFD`spGLTfpNW*M!?y&qmwM&$vUlM`+NP3@PA&5ma>b%p+ie5Q_!*yQ~Y1@ zebfvDw|~(|_J{2oKH2tJLO<949fth*1;`7UN#ax5+?1N_fBhh)9 zAdz;T(~Q?lir>SEwIw=rb=ukLpM6mtO#|6EYeM;>d~@AQb;nzn8&%|Vn;p+EvuhjQFYE@8G-{tYXu*lI`ps7K!rRA4}_b5UtRqCA|(svPJkmL??_hm zJ*Q_nkq9CP{R6+#d)ai$xCXcZCQa26gXtn~2VLY(Kv6f<%-gg*al)ahZ_|2ba(3cQ zs4jP?4xg1LM@WGkf|RiG@6V=emLKy!KcG_KrS4mIc+Tv|ptz-5_Xr=f_v4dId5VET zpry@f9WyF`Wg6p)OJ4|KS`X6!@g}7JvE@=@7;#u2`yQff(g)C$!scQ5ft)MDcgndh zYFgmL@%jX$qS%kS*H-d*Ken^!2^$Qf&VCzGsAwH*Xt7qW1|8jg{EEOMWVmhwviS0n zX*&1ulj~aV+z1)U$_R_ul)--29;@}qq%H!QoImp? z59r=M?H9bpJRrwUh`+%-Nrp!iKBr-}y7iwqyUqP<%#20<-fyh*Ui^kXW$gh7jb{?{ zMtJ)187SzS3c-E)cO$^vkjK505c0B~pi^RrABC_qz#;f%a1U;jGz_7?1ygV)DSfa@ z%;KhX#(vv66yG?k*xUlGqUEt1H?dTdU&fYiuK%6$a;tY7j;?7=q&a}Yx2Wk*QLRVq z&N0#2SG+>{>=`-RP9lZSiHfl*#S+IH=28(!3rN`;8R79Y!jzjGH~%UG5p~T4Xd2E? z8FjSEt_U^W(s#nWE3*hwVw@YHvsE{mIti-YzB@@dlFxWl4rodfa*9J;9z$tIV8;qC ziAnA}@QSZq=^OJuX(eZ`XD|LF#A5KR`Nv0_1HlkiG?-N0DP0*vuQwyfk9oQ$<3^NC z8dwQ!se#KXYpp?WGO2D!t&x!n68Y+6b-#&XRcZMBNQ^0fFeq0w>|62SH(D!ba>IUd z0nuL$W)XiPtKP4;iMhD-Q*H3{#%t9BIFVAn{aHO7zWaC9K1t{wWB3oN@gN^IS4 zaw*1srMbxWIgrsnNgeL&BkV`fmO6)5U#HQQ0vZNxZ~SQOx>BlK@s_GVEL99aD@g<$ z3gvv35$i7jdV-vLMlu+0%{6W0rskzvaMEd;a8M|+^*iA#_(?ouRx$7Mg;QGa{s4tX z6Mr;FtU70TaQBFq(~R`qH`e1AT98C#O?G0}#V+yg%_Jnu%dcfqSc8VdZu~f6>T{Rd zL-nrS7@kOyhr|&QQg|$N@bLcQhht@ zlt_{`SdBi@Y@EkQ8Qd+UR*QXG!3hw0?>%`Jq@pXlQc1o(-~u!JtTT1nGV76hBaus% zpB0{lNb-7&$n;)&zh6+@Jd%IE@B=TrYl?*0${LMFazHRX(v$Vb@dxHOJ|KU;P;J;Y zvK(w3k9-A@^~m!FzV@084!X8kN&8VnQ>!zrqC@mo(?!DA^hH~Ig}SVBl7UF}zuhr< zrj>(PR5f+6D$=y+zckqOvUhB?E6)ZkwQUG^gVI^$gAE`>^*UD(CdO!;3I|$*`liju z2-8t@rk`;(>R%4(oa-1)q@r;qo%}YIlbn^~&jvxq4jVH7l7_*TTwI0Mh54F zIp#8qseiIS!W6Ig0m?M^#T@&E4fz+(e4!CKp0g$mJP6bvt8;^wW*$<8Yo(JArHSme z5JiED?l|OThrMdGw?M94PRA9(Ypz~qt7?RfvUKyp6f8+k^!NsAe2m`oCQO@|FW6w+ zoun{5j$2-CEExwx0>mxTjE(<*>#ghht;TCm=Y{be{Y#I$vl{i*!o&x`;Rj6G9~n;V zwBZROyr(>LdgM}O7AnZf9_It|8<@A*p_yt)!fRu>&M5DJq72IgGeK(;j>j*MzvCpH z{do=4X{_%`Y_4pk7DdP@&1wf+^y)h^N$pAwNmwq=8*qCEuK{bb7TQ-qS|EKoX~U+u5>Od-^VGzbhC7eeg~Z6GLlj5m9|~5 zQ+?AsQ(lX$fydU)%u(4VMn~i04rbwmR8wAoaIfl0W>Rml5I;0N&)nL<{t3@@wb1mYwgXs^> zd;|S7e+}J+RMPQ~rukd@Av-|@)?s2du z`Htum_T(5XEb(y%{tA^SMW7AUCMwpgrVWuOS{ZOFZ$sCQopdXF;9HJjam{|fI3v_I zFKk0gjIOMg{Y$7GyZOt-TqqZBWt-lu`}_3xGNoWfAVxL6o~d-U(30^Eg3`JcH8CGygXN?|7!48UbC?bWUbX!oF}YMfeLxaY zH8r%&!%FU_n=le`6rX2m=f9MTadaa?8v;%d)0>G|&pzmEMB87=lY^GG_frSeaD4Ct z&-%G`OyS26Y;m#7Lsz3Bc4*+o2L)9{!y1PejF~8h>36t{X(&hCFKssH__fDY!vco1 z@!4}ntS=prsdJm)wn0xVt8WI>ch7VXq8tl?l;g5FsHJ?f;AFVYsHaJ=Rins~41f?~ zd39xQChc@b$R`q-|4xOwWw9H_H$5z zGM@d8i7_-{&};<3E?d_W+(-#PJW9xbuwR@ZpY)FP!c$v1Lc2h8lN4Ww2R)kGj!RLQ z=t*cHqF~CS%D7}AtRZueGLNdcJ=IW}$i0BSn0xY54DNHDM(SuiRpt?r!X>Evq52{k znoMg-Gu0gfx+F!8Ug1*dlEgexjl*}@esjr>NqC;5?GJUmr_@Zl$H4=rz8VI~wEf9& zy??uXrp=0zXIB0|J~>1b-ub%4eMw(~ySe4FW3)ltvaNSXafZDKfjq%wA2hv2OFe2A zkiAkTUKil|N-K475WKMQi9^@}S$;f7BG0i&570Ksq)!pH>#(OhBDW&j$#|S7W8#dE zAh%}OCdsM?$_&9Hw^9J%stoj52f80Szqx&slgp$noH9-gvMW=tw;UrG_llci-aX+B z4A;ZWmv<#@ug4~EZygk&2FAP*eW*X2i%&dcxlG~h(${Xn#J#aOmE^RC6_~(ewi(y2 zSGUQNLQC%PSO}A-pbeB+I0(imw4+7oqeTJfV-9Et5fAQS%j{~zr4G8)#eIUs?I;*V zmp7oryCYhUX$k^*XNehABk+E2gm-OO)A$6HDWl$5vazRq*-{cBWZ4zmpaUX}$^yT@ zLNMGxlqrylHc|4KvGMR-VaGK2EnPplpXs*`8vI)xe)v5p+Lt+hk@DL$nS@N*_Ed0E z7G!c^Y)=!vWa>pt(bE~DqF}TYWyZAo(bBttZe_;Q4C6q!pj56Uc5mFQd|f@xjOWDF zB5m}6QL&p&>lEE*?VNdZCCRFJKFm8R5ICUcy_lL9+fGl~&u^TR&(bkHdWS4eK5&1$ zm1wwURKXB#c}P;B;@BMBt}U)R?^~g)aB@|t1O}S_!$nhbbY7TPvb`IDPQ}RWGE9PM zI)FyTSZiW#K&en}}sWegx-aayi=oy33D2)yKnMGLz zxi<6UruK&NVeBz$_&=qNUb3A3To zDpOmy#zzJx`XlB<3l$mip7^yPmG&k@1_GU6B~Oo~=eD@jXN1&lEiJ||68zHRA7=W| zHp!-MvFLBT<(%6j#5P6%V&$j7HI3@J^ zUh7UD3$r^;N)+9BNi$^E-QNOwg6kgDYd9ySxQED`Pfx7%LZIECuny4O-{F4*T zlLFUbd*eBdIbn5#2$R#&<_S-aPq3$Op0 z-6_H8H0fp_Crz{$xeT-|qF0EVlvq0urmOtgur}6{lctv+o~VN+M*Hw64VdiQhL&z5&P1azKgz}V>~L1SK+vRZ`Cul=KOB4R&{%;jOk-1%#G^l!$+9~&OfSv} znPn8%mq&CY+F`84GhYGCmqXKhAKxe3HHtj87Op;J%>w5fKWE>jsm{>afJKwvryZBI1*4grygVtSQexd*@iW6G#tf7)Cl^*|6v=&6IsMXa zj~`iKFS7_oTrQV=%F`NK=5{7ve{xEul4W?mi}OUjy^&G+2P*U|t@fI602c2H=(f%$x?QU}S{$_3G9AM|%$m>%*>YaNxKe3?9 zat?GidG4d{XIGg2`ZGQM_e60#=W1TmOoHXq#~Ji|NG8wj?m^FI^I%~utXIvKmREAI zu6(^78pWxLZLIcv$yApa)eYM1jJi<%oPNIXq2V)rx9t^mw)xu6bX-f5>B2R=sTm7* z1E9d#`P)gHFyj5piBc{7+GN2!e8|`9vKF9~r_aLOcf}AbmVc77K$V_~8X6QLwVj04 zNj6ABaW`N3OPz_nD=OhY<%&_BR)1QqN0>6=2oZn5BSRP`jX9EZDef(E!ni$Ug3dKp z+QeuD#0_TOPu`5}q06#Uf$?q;1glb$th66}AM=o-WZr%>gm9!ITA((Vw{++$#|8{b zERpyc=0vY78RDmZ>qj{<4lMsRh-IJYT6rDu(S+K4DFRf*j{CecY#!C2gRTvYX7*|q z{O}AWud2cRQo%BebHvOgZNm1uY%|*Lh;*(Zm_v98%`LJ+4^~)%M^r{OM1LfAN!F}o zT&)+qcSKmPRZZxyNa|L4gnM6MGz>f+@z*OpON!Q|fM)3}Y=P7|uB*_S=3>6V=@p*EYu>q(S>o2BFRB`U@%j3|opt!?uY z7PO{|_uqpnwVUdB>FX{8>qSKpiQ!FeOie7ylLU_3hT9 zyM(B^ieS`U1ENfQl!b&`cZLAU68Fv|$GjsAUN500pghAZub%KR9q@*^7zjs@=SU@} z(a4u36>5*oz27~+2j#j{m%H=`0MSBK@2=%<88y! zLa8a2qWyUh14c_eWvt{HNq9;Gf2Dgzzfwdx;}arD!@YbppepfPaOyayQLC0lV1Ok2kjqD>4s-%{3`60@`=l(!o=K1Uic1%RfxE z*xTFxRI!Caf>%BM8(v5+;_e_xMdbKoxP4<_L(P8wKru7+QKV%cCvTcjIF z+huymd(xSEmxrdT3ZT#0fT&!uIKtOpT2I*_&6xE&?NYz>y}t8vvbEF5JC7x2KcyVJ zv9x;h9$S9?WadF3`}AMf#G+rZNU&GEUG zHzQ{L;N;UC?EBPbo4oc3YWJkB7-||3y$Js5b?q!^@iX)2I|lyHgHqH*zXWxvgWrIK z!W0`ZksVWgIO5caoJ?p>W*G<&XU=)T~s8Px5;n${Hxo z+;&hT0tc9)`H+Xov!Tt~a$mK)NTQp=)e7hifvUmc{ubM_a#!ihFr&_k8i#bN_{WEv zh=Rl(g_|X)=S+kbVBffows3b_`H~NSq zUD}?37-=ZM%eLDUF(eEU0*_)0F|@vPqO+@(Alg8z_Mh}-zb-`Fw_{y6{ z@KlSkOMdMIi9&B8`S!UNNBgwf6f4`7g2kv~JgbLX`uF=3Bvb=|eLp7B`Aq^~4UP>N zJ$zTfvo17%CtUFlip+9sQUnzKn8@cZhOX!5F@{3x(s=t|ugcaB$Ba0$>d9t#a-qSI z=zm!Ka(k`+-Z+IgMUjV3HAL<7Ii#mOF0{hkHB^~esnv3e1eIDW-NJzg`{+2N^f-X6I@~6DFwK8zQMi_8D zQ;oRkiTh-KTA*m+{1EL2;J0@XA6;2Av4lvQ(2)P0km_b3<1rQz>`#MU5)6><&wi^1 zSY_!o?dv9rxh(*Rl68q%p}43zFP-CP;?hKW@W0wo^^asi_o4`LI~w_aj?F7@`rniM zXYXlIB{d$FyNc-Q$&UFo9y_&w{p(_dI4(U=rd_Hiaz>>@lY_~dz#6UHwh8WF`t)N~ zQ6K2X=sn@dv1VxU6H65A(pMS1KACspB{YWvHwfRno4A9pTtce;Wk5<02q_riEDVh3 zj@kynXY_t~&w0J|`bI+;1yvyx8X5LYzS`|DSV&D|6#WOP~ksh7x@1h_sclpt)u?4 U9OcSzypVu$3S`XOH~+@}0HNE)`Tzg` delta 9236 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 diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 26fbaffe7..1f9959196 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -242,11 +242,9 @@ public abstract class RedBlackNode> implements Compara if (!isRed) { throw new IllegalArgumentException("The node must be red"); } - boolean changed; + boolean changed = augment; if (augment) { - changed = augment(); - } else { - changed = false; + augment(); } RedBlackNode node = this; @@ -569,7 +567,6 @@ public abstract class RedBlackNode> implements Compara parent.right = child; } } - parent = null; child.isRed = false; if (child.parent != null) { N parent; @@ -590,7 +587,6 @@ public abstract class RedBlackNode> implements Compara parent.right = leaf; sibling = parent.left; } - this.parent = null; if (!isRed) { RedBlackNode siblingNode = sibling; siblingNode.fixSiblingDeletion(); From cd4424b94a2aed95859dd6ad34e28a151d71acec Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Thu, 2 Jun 2016 20:40:35 -0700 Subject: [PATCH 11/24] Fixed calls to augment() in fixSiblingDeletion() This fixes fixSiblingDeletion() to call augment() in certain cases where augment() returns false. --- RedBlackNode.jar | Bin 43393 -> 43546 bytes .../btrekkie/red_black_node/RedBlackNode.java | 30 +++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 1cb1d9866eb60a42e3caf5c9481b16f0f2967ffe..c6adc61fb40e049e1fba270ed06b5518aad9de22 100644 GIT binary patch delta 9966 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 O$fS}Owmk12>Hh(+oD^aJ delta 9741 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 qu-M^3LA|g-LGk>Llx4)> implements Compara private void fixSiblingDeletion() { RedBlackNode sibling = this; boolean changed = true; + boolean haveAugmentedParent = false; + boolean haveAugmentedGrandparent = false; while (true) { N parent = sibling.parent; if (sibling.isRed) { @@ -480,13 +482,15 @@ public abstract class RedBlackNode> implements Compara changed = parent.rotateLeft(); sibling = parent.right; } + haveAugmentedParent = true; + haveAugmentedGrandparent = true; } else if (!sibling.left.isRed && !sibling.right.isRed) { sibling.isRed = true; if (parent.isRed) { parent.isRed = false; break; } else { - if (changed) { + if (changed && !haveAugmentedParent) { changed = parent.augment(); } N grandparent = parent.parent; @@ -497,6 +501,8 @@ public abstract class RedBlackNode> implements Compara } else { sibling = grandparent.left; } + haveAugmentedParent = haveAugmentedGrandparent; + haveAugmentedGrandparent = false; } } else { if (sibling == parent.left) { @@ -517,14 +523,28 @@ public abstract class RedBlackNode> implements Compara sibling.right.isRed = false; changed = parent.rotateLeft(); } + haveAugmentedParent = haveAugmentedGrandparent; + haveAugmentedGrandparent = false; break; } } - if (changed) { - for (N parent = sibling.parent; parent != null; parent = parent.parent) { - if (!parent.augment()) { - break; + N parent = sibling.parent; + if (changed && parent != null) { + if (!haveAugmentedParent) { + changed = parent.augment(); + } + if (changed && parent.parent != null) { + parent = parent.parent; + if (!haveAugmentedGrandparent) { + changed = parent.augment(); + } + if (changed) { + for (parent = parent.parent; parent != null; parent = parent.parent) { + if (!parent.augment()) { + break; + } + } } } } From 8c98d5cc42528589cea26e6f92ec314f3b4d275e Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Mon, 6 Jun 2016 14:55:58 -0700 Subject: [PATCH 12/24] Changed createTree to set root's parent to null This changes createTree to set the "parent" field of the root node to null. --- RedBlackNode.jar | Bin 43546 -> 43568 bytes .../btrekkie/red_black_node/RedBlackNode.java | 2 ++ 2 files changed, 2 insertions(+) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index c6adc61fb40e049e1fba270ed06b5518aad9de22..7e987c9cc03202615763ce0036900ea49b5ad443 100644 GIT binary patch delta 9918 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 delta 9886 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 diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index f9cf2602e..70bcb229a 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -24,6 +24,7 @@ import java.util.Set; * * @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 { /** A Comparator that compares Comparable elements using their natural order. */ @@ -720,6 +721,7 @@ public abstract class RedBlackNode> implements Compara height++; } N node = createTree(nodes.iterator(), size, height, leaf); + node.parent = null; node.isRed = false; return node; } From 91b5ae633abf3e4c42904e59209af0260ce9ae9a Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Fri, 17 Jun 2016 21:50:16 -0700 Subject: [PATCH 13/24] Fixed SubArrayMin to check children This fixes SubArrayMin to check the appropriate children of the endpoint nodes, in addition to the children of their ancestors. --- RedBlackNode.jar | Bin 43568 -> 43619 bytes .../btrekkie/red_black_node/RedBlackNode.java | 13 +++++++++---- .../btrekkie/sub_array_min/SubArrayMin.java | 6 ++++++ .../sub_array_min/test/SubArrayMinTest.java | 8 ++++---- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 7e987c9cc03202615763ce0036900ea49b5ad443..f44200aa713b85fed2107694d8d5085cdf12b7d6 100644 GIT binary patch delta 12768 zcmZvC1yJ2w+bzYRxVyVcaVzdlad(Qlb4sy;%fa2gMr8u{5-+%7?zJG2e zGwa!FuRO_4wq%l3I|sQl2Z^jA4+V_{0S^xk(NsMhk8B84UOipsV1@7xHY_0oe)t>c zf0Tp#8ZDR(PaN+P%wR9{xXqxrO^Kbpi*mUP%Rz-2(|#FA&8joUs*S4=idWC z#*Y8gqJRbp2ph&IVg4S{Fh>jcD+6gV$N>}`XFsF)XB0Z8w^K1IJ~%+x+a%yrPMPJ) zp#@^mH)2GO)aeRTm=g|G6Lny`m*KLI@sdrv(Dj`T(4$4ot*qRg`gMA9X1`wDobW(A zm*UH?ek5gdsCcD{l{(aDul`p?2~<9D>F&TDG)EY1~`8@dF76P<)|cT0Yxs z1gjK>&a2o+6$YDI=Lo2U{DUJ3yos2hujkS9Jore`m=t&wE3b;e#j+3M=Jr+KS}!Iz z5>!g;S7E;P`7B478!~+M>4pUzF#v)mQlgb&W+aC*?r)&1zT~78-IsSsHM<7fwP=Z; zyiEP+v7M0Xhl+9139G7Vqa1#)5@bBpS8p~!Zq+_S@wcT|Na@aahSiZAz)4$#`Qa<7 zt3$i)qE&e-j-hYNiE<&27h`YUCQq$6rZYIuluaO;_IvXvEhf8Y2%`L~E+E%8saR56 zu|(2;6tC;2*emBXsjN&-p^&nXF%&)r3&>n4%jqM`6URKowf6j53s;fa!+99x=g*U@D)Mmf@3<7iXpj&P-=HBN z{$Hp4r@xId$pJLxm)|d#8Yai($iyM3K8U5t0tqC;ze%70Lx%>7^RdIszgf$WrG1Og z!|ty-C!F&quacBsS8;ayc;3`>{`^D3zM_>p^}>I$%hd`a`Q51d;^@ZD^jzpS(}nk> zZ@`7ec@{08^PL=mD}M!!US)mmN2`yCyo>7M%kkrIOdr5`u!*W%0Au`SzhxjJCS!47 zBeOI+-dZ>=hv29OjycZN&QGVaool1H_G#*IkS0Ar*bC;V$dcDQN$?D{5spK7l}b8y>f5l zGsUX~zN;9V0PZAFIxGyrVM*pJrhP+VCG7+!sTjapSudV^g0b_{w!ueSLhl(LS%|i) zi3thmgpN7SiSU-@IO9&{EadP9$bEkaJRSJiw@C$zJew=(iI?_*t}N?0ji?v=j_oo$ z*)_JuayBsqLsuSC8)tdFxbojfX1t--t}kBa&7OG6Ys`(o@U-a|Sy^Yq&`lH7#TtA| z)7gM1T1@1|Y2hezI{AXKA5C0!Y#X|Zb+wVW0aI6s;nc&)_uL}Dq^$-VYB{g#g!`C; zWOJ+V9vRu${N#oGgye|?FIh%e3g9bS3{SGLno4}&T-tCe z83*Hshlw@pSw20?tuaVXlA(3AdIP5cD-au7#2eeyb7FH6eee=-+v?zUKoeh6M>pUT zIuqWPC{Xjgxom<*#(Z6&|A(#6eYsi*Op~$xcycC*NYqHR&@R+zm)P9;6aX2+qZLs~%>s7rBt3t+O`-<$vj^>`!H(>*-rI=i!i_(5p`C_KC5Y@+>*%N zMi+H+Re{FRV#6I=J8017#Yo@~7#Ug6t?J66bppx1V&{HxGWR+V2 z613W)JOlkr1=@;e1{U7lQ~I(kS9Ovp@6oEk$in)YgA;}PVRZyN>2U?wz8gdXNv$Eh z8HU_~;?*SkMGcOrM{-dgHv1W6#y(xMZV-pfwUu>6pP@hE9Q!>g^8l{jKhop&cDzQXMFf}O+&7(!N8NRDdZLgyG^^vKod#-uy| zyF&WDR?cS>t)L9fDv)#aH$O5RN3}&RTAAsyzLo)|q zgB)F~vka;-8ydO$U%i4l_w(!#q@-p<6|MNKxhDfUsa4oV?d=^lO&F~kwj(iPD7;t7 zzTd#R=9sh6nvMMq;v%3X|g|Y!u)d*bgW7 zhet*2kwSuwX8;vM)3V&(Av5bo5;#DuPCY0BrG4QrgA&vqbum0Nr)L)CHl{_NN%{uy zq&;RWO^WU1iIvodIEhX;kxs^-VFD7#mCy79?%$PBK4SD~sgIQL^38UGAK)JY{HWP} z#B&27WeV=T4Bs;}C`f5oA;4)JG23$TeRAwh`?+CC`U8Md`a+)xT?e!2qWRk(r;-JG zZvw%DvH9^5YOlLXlW4%>MaDXchv&ldP=Dk%Og_?Q9D4-sT=UEsq+YuITA~Op%vJj@ zHa`(%eT@dw((Q$D`p+iDJ=6|y)479rd=tBRsEc-@gswgcH?enYmW1pwzL@yMu)>er za4L2?>c|6V`?<&cXr_IGhQm^ie7q4wlx=N&ekz!!(7rnU{DzYx9C%oHB7=v-apG-) zBrcrt6e5WdC~-?0U{$mR*^_4dEJ1FgmL6MnH6ht2uY&YTJuyH4%%jd^S*N0%Y30eb zT}Sf|AsFoDwW>h)+^2bEMOZSsJjSHi+%>ii8|6+y`SIkjjyoYe-{{kZ8Inlzt^&rs|+fIE#$$Mr);%Le>LHBebUUGxT zK7>WuHyJ^?Ngolllvz6eEYa)6dh|v8>rj$OsklR@xKg>hzcrWnRD!@%0!x(%i|x)B z=j)w?tsyLZ9Tfdm0>ODb*SZNKR@cY9jIC+F2@-%m$XXqCdJ08C9Az329Px{?HXewv zdcV*&##ExtJjx@oGKCB{Wzpciv~Znlsk=M3dzE$g{Lx)7S#EH8fJolAeQ9iCljMAi zr7R#Zt-DZ5uWWSyXdD9>xBBZ!)VjQMBd~^%_(!P3MGr~E7W#=gQZi@Ip8jeBbt88`D#kVNg5Mq;i_iChtV%IY`PCmN8lQD!q3z18pf9YKwk*g!2 zx5=$5oA*axp%dl&x`djx{EG(&D?nVq9falZIw@qEETTZ>M&)>X2L{mO75Wf8R}_S-s&v{b`EzG$vT%B_l->?_NK)DZo` zR3aP^PJ#SNx*|8pHcR_PG=*KR`-VJYTMkt-y6?`1sDAO(;4sx#=_#NzJ7Me=-V1u< zC2hSFfF>G{`RO$rNjCdb7io9(lWXFV4leLt&HkVHDU>!ZPP_03vG*Nai7Lb$q zcKc5Ew9^9Bk!<1}ibTuD!NkK+NsccuOJHU9q-3FeBLdpmJbV%yzJ$@8jcg1}v5~Ba zLo%Yq|DnX;7i9iGGknRMu(e7B_LK>|NI=@8#1D|+csUdy2M?#3j;a7Q6sd+V5OKhB zTrs;Smc!W5jJn9(I#!K!gp*&UM{M0CZpJaM;;gOvzAF4Ojix_0`#Qg#`t|tqd$H}+ z*InsPssz$SpZmZ?z%XF7vCG~UTlT&vp!JO6`I3{i1kVblN5N9uUbCQstwKwY)Niu{ zVusO z+wRDn>+l)r<>Nx{PeC0!8AA~eM)43WQ`G?Ri5)$ppPqB~P<*NOjf|`8kRk~dhdoq? z@Wlv$d}7;lc}_@tVKBWxW+Axpbe|5fM42$jGZ}h$rL|jC%(pNA@zE6$EDq@24_QNl zXZfV(7_V1kMJND_5HEd+%S5`~hI~9$ps1H5(!fbR)@31@3q*E-mDAb`@>J(cE@I zU_{D=-$UaFA&|QG@Ff&KhWiMm^)~48%q;m&IYle+fPr4sko$>na^^EU0qoU`Uk zLhqRdWnl^=B{CAXF7Nygm8qZZ@VCU+s6`o;F%%}T#!|}wH3EW5I+9bBrdFKe`DvC` zl~k=@x$h2{Jh;;Bx3L_z2mI&>zfq9vfOkdyRE@k0X4v%fHa<@w!@_btO-b$c(ivpl zBs%qTe$HmvQcIyQHPZH9I72Odun#w_#LUQ5v6A;sHE-r|chi^eUy9}ou!p=IRO`yXtphF*3VM#7f zBZ?L3N5ErN>I>MWio&KXP|6Gq>cdZE(KKxa@Pe3nb+qX;<0W}BDtq{NWiumZ`@MPf zGGlN0U|JRQfhT>E+Kem%j{VfF;`$NM+Z=)nEQ6NLFxglYSGaYs0L;Z-)Xr7Q<7M|G zt*L&*_9&W$(jC)ZS*}nRSDFT+9jAGL+efNW6(#CKZP4<>1{t7Zt85C156|)H2`?M9EgVC34D36xm?{ZG$BM**CMuNA zSGvTUEJI#Q8^F?rBURs7{V<1)E9;eL=~)NCbeEl`4QqDva7||~V>Y}imCXPeqF;vS ztn=}dKmx47ue?*`?C3#+Do(@r=HiTiG3s;uw0O@V2s$Cm>AM$R2~fIL zPB6l?->g;U1-+u5W{r#+H0nj<89_U2AQ+;!ord&T@EoF7oP3P~3P=XyHHr)%t9K?1 zA-CKA=tQf;_KmLBzK;(o9{~XhgAi@!$U&Xay=)l{P2tZi(q=&(Ei#$b4joYO@^#|v zY|g^Hj}F2@$(L3mXH1Z`tCCsvZCZj2)5f=-+L`m>;)j9944zwY5KcxEA2B21aq?rE z5LAPVx6g;V(Vop6Jeh9qIM@QD5>Hk3yrdFOR`zU3O}7zFBTEY^)DwCpe5ix1k{x6h zY~$HpA5-ncMrR#l$Zne3w6)^=pgymXn>Kr%&DZR#8yR>W1vVv`PpvE_gjzeQ!h;j@ zs5eVx8(X58rI}{kAJFR*j_@^gSnFqOJZLNu*Po2>;x_R~cQ5Cks_h1Z7g_pXAC4`U zDB1HMDr6x9*Jq6Ytz0Lq9GE0j6^IRtg0WcRS-tTMuMi=(xgZ}-4p}!o*Lh(UZ8J;s zuP%vqiYH$hM4?}7L0k#z^q{g!`UMB|4+0~V=~$ky?6*l}A|`9~{Y2TPMVyNtcqVX- zwoR|Fb$XdwHT|M9`gs6{IGfwtS3(PsRnYhLtzo92Vg0dQVz<9^I2k_px#hiUv z=w&`v{-oEKp(+!@{Ng>dAH$}aq@|%sWV+aPzc|J0esnFHCp6@N2731T1%HI3c#V>}pQH}!yER{x}~A%6xK#^nd4>0y>DYfhv%g+f0Q zyhMZ4M44h>g`$c=_MkSAl1`VA;>LYNai6DH6{vh3t=|S<4IO=D6N?emSjg9n8N~C3 z7#Kz1l~^!o7ewRSlNd_|IR|HcZ_SL5tS8C3Hm-~d`-QdqtzkEhS6OM0+68uDD6Lg= zqIX>5hayP~@-@65*}*ouH&%8+_fMm}X)m1#ux$c|8vfv5J;Hn9cp)=_A1&@LxHcKO zWs1&gUXx)!2!%2W`s<$BOgaj?hDSuHLPE1!TyrJb^C(D0PYDNZSiP_3TB87+5g{C! zP@0mLyK!BK%YwzjMsPmMf@Af_!G?QJ!{bOULP&goUg$IX6N9=>l+TLJ(CDpO!gN9` zePXS=(e&oBRG8da9d#Eky*po>z7kUk=ICCZ3tt`JKjMEvkXi~RNe@5Y2Pl`DrmTZq zHMhQW9Z_M{obwM|*c)fSm8W)O6V0jI-vTP{L?u}bxvo#4N{xsX`R(`u>Z&sz3Iq#N zle*j!zQW!w$QE?Y)n~(J5xQQZ6PEd2v5efnmmOfLu}bB2LI>>@1<#Gb1Ikj&Dmx+F zujLdHwDJt!2xfQZVPQO8c)_AQWZQGXIu#!B?ha+ynn}W*^07?rf4ZK`D6vf&rMi`K zaL5`H4r^puZ|2Pvqn0TyUU-&x#ddCKgEg|?my-g?_T^dK%8ZKh{*c7-HEEVG4h_+P zTsnh`Xs-x=`P;k8BM06=z(<<+yo|*qRLWsviHuhEZFiEcdrIF~FhzEwjBUIm(K-q< z0JY`?3UQa1F5WX=ZbMQIz=M*oyR`>9mhgL9M%FoZ4QRF(q)kCO0zBbI$ks}m>cp&| zx5F)N43(Qn)Z}wspWaW3rD`;46oxtHye{K@DPr@7rT>VdTriqGHu_oft zZ?+PmjAJY&Q%H{MK{UfkHZnN&qmQjC>)Eyn+j0U{t!bYHoU4AenywTr`EdP6&8H96 zVo=Q$sw!{~{84;hd+Y-Ge3hHJ+zxEUEAk1ETt9|NtKG__wrM8pMN8Bo9xVC?+V!Q7*QK~3iReM=4o(wnH} zsq4xUfK`R2yN|6c0~stb@8nkl7@01Rf-hqTHtLs1F2{QPl0>IDpRaI6 zp8r+3E^KA+XA+1D66l-Wf*Rm>@T#HfD0s{kA1Q*@8iS_O3jX#fG%$uQHDZ$9Sx>G= zJd>YQ_wv&@N0FvrU`LM#{t*R!z2lv_i)XXCX({Wk7FNqQHP3+2ten?Up^CkUuWhb6 zfOGw1ft0-oi#Av1aO{1u+tKkybn?Me=;1eQJ97#Cg2tO+GVi$$N+rKEv6dZ0f_i#6 zlmmWDH~Q);t{U4-woDL)(x;_+I2tK(VoyX4Ek7%$Sfi^)X=h=-rP{ETv;3 zRr?5a=|hN*(-#-@%JW-zv)&Hj4=4M7Z-+}?rs$5px@wMO16ium(;{NKqZWK5099IZ zPeb~^m2aiU5i2S!ncLdcPE@VKS1ko#2ZN!ihKM_M^w+8AA?o_4)Gmc&zmL5hcXwW# z7%O8xQlcY8ws|Ic94jY;(7T;6-d4V`bJ$fmD0D9kd2Cv_t?uyIOdg)943CsjI8czg z`k_TYd*%P6xqb_j0V02)bDzOO02IZtw+)Ra&7E4!?sO{c zKljHRsXT0cWbUQ$5$SgPAf*YUtZf7<;bv@%3 zPsT~?7Hb%EM5)Q*;!4eBrTsM~^iny|-`%kh)U7ra)$md2C)OaM(6Aw=-H zUUWkiISB~dF`<##s1r(V&!7`17z3$;E-%^1Ntqn;aCA(7qa#&}{uKc~xyW3*J+9`@ zYRX3<=|-BTBvGU*Dz%5D0MIBgE$2rLD>X9ZPl{Es+NN{I*)RK&S~Ed&;DLfu=IWfu zk7|GH$5gcTuHgPbv*M$3PCFiInf#TNJASrW%8s`?jG?O5HnKZ5UnSRrj6pQ(JlBJ$ z0b}PeKf1!14LAQsTtLD96CB?_D=QDBPpZf(!3ZB_AzQMk2UXj(x90a z*}XdE^e%tWuU0b}x|tK%qdu1RE_#xC)3uN1DX-b9Iwth)c+%Xcnn*z{Kf6}FD_AS^ z*#!|?<1_Ex=#FSLB{e$Z0f5W34@%XtDnd^LzvSJ_ z)2%C?6@1jFI8DEe*rZcGApY<;zsZ2iMXi`J{a&T;NOd+(!n?M*3 zE*9LADZSO&k-0{|1aMy2eaP!m%AhSj@*5U8bmfVOL^VgZ z6Y~@Ir*Ithk9z1*-0isV(kF};0F#O;LH0~y_1t@OcDch7$COZo(7Gc z2(MYpFVCdr4q4b7aDcqR|9lf_%tV<7Scm?-lYg(HzH;Mm{-_xp%f~vw9CJG>t)WMU z4V?FhfLoWqpRuktOLk4@m|0c-#r&h}pu#~S@(k8D(FV_)z(qYc6Kup#NewRK>5(r3 zByAlj?hi*ht;pz46~q-)sFvEYsa!;JbfJdM`X_GS`X0WrNyYYPw=2)@I0J0qw%jR>!cJH6>eW3^mhX+x$YrJjeL3b2P_=RP@-y zRp3?FIVk6FsAFWXyL?NsF{e_y%I*6*uOT>b)7otdaBR%F3S}&`54RbgeFvR>*OsW#4ij^|ng1=hv#3 zzIv0AwrW348SlX!7&kjA8q!-JV_}4BCaw% ztaqwUrCOoft);;5sU8pmrE5SZUcKP(v2i?7d#2{_;IY$CiNGJ>Vv+&Go{{S7(Wj}e z24T|GvnNPaQ2EjO+4erXoX*Qwi}x?!w*asl)bv{;xM+L(I)DRb%DH#XuXnEwuP_#j zwjQI_g|*iXMS1iiR>@SP3uy;%=Q20#-)?u+Q#TYOgUg;^@68L`b0dh`&x0vE>($&nOXtSfECx5p&GlvqE{^YY;DR-W>7=J@BVP7H zwG$6E(Z$2^A=I*$eE)X&Al9E5=eHYBKW%}+8i;7q|;rd=as4uMT=oteKdWn9$qAkC+Ma7J-Vyq)u(w{0(l=;6ii*jrJldj z^D=?@IldoEpNE8vC&53rf`k^ht=Y}tsoq<`J?DyO`wGTRwADTM&gXj`?-OB z7+(W)OZVl?URfFgP|06~5$>U|4pC)mN#m~`sdikudt%4{F2-pVs)UPf8*b=GbAo5U;2-wBV(_4;1MmoG0ivP$ywf@_t7J!VF>(3g&uU9#vIShh3 z8xGr?Wd$?9c+|>$_f@NP*oO*Q+sd5iSnJyk}n5Ku~>Uaa~?J#dbT{hSo2uz z#HkFR!@@OMWZ5<+XYiH&j?3x*?ET)1QS)6w|2)Dgc)8zVzk!R4XW!3o0WCWIY#d9D z1Zu2-^lTpwTJV{2kdu37v{m18t4+WUKjnmla#2U>IE9qY-OEUWSBBuoPXiA)KoZz0 z9i<;Ohw6B0lm!1|YO=CIdC-y_m9rz^LS3+gP8e5`Zb3 z5J>kGDT(e)hJu^{2OAm@NR23p+@G(xMkQaLV0UO4)EIlVG&te*w0m2`ubwq=n|DU$ zCRcJ_#Oi;nNBRVJOL{0?($uuMUPx82fS-xbaggbI-nRL8?s;)NE3z4YnhV!r{z(Q{ z92ugm+2XRN!DNxP()+!-Uv6(M6Yw?tSilu#9Y{S|X0oMl9EubMy{YIf8@>01k}yMV zeQqWhI!uS@dY-ec>}$BG)Je)O{wl(DF1kE+tZb7i)lebpJ{%a1^tH=;NvVe5f((}G zpK!R0N>Ru;?&4wFt*lqG3t1@&F9ejFSjr zuJSablCbS9eAKNAoaj@G=tf2_7+>DR#r@&M=>tvfrZIhnnqu62q5P|C-6qVYprw7|Dg4Eixl2I(YM?j(44z1j+mVve z%VyA1zoG1}kEf4jE@_es)EK}=5U>rG9i59lmC>2B`qY46HT+G8=Md0CH_$KDkk>rb zH_Q1Hf+gi_*aJ<&Cu^WThmLn@C)_0C|9-ddpmlgi|&+pHcqEc;i%zt zKAl(i!GpK_kKD^oWvG#moQNztiU^9DbqSOvzK6?cuOvuj`gYZanwehn+GrFr{g$EI zS5Eo^nZf?n;K4#v_eBc+jKJ4)$W32x_mH; zm&rqVSU7k4bsbS!*0!eNUuJCv5jGbBX?2(~Y|biKZqF-b9a{i&lTecqhN~C^eb`c? zq8W>nISxKSQSyF_@myXMM&jPUDx+z~pWMP3XKkIGX&oUKXA}(yvD~M%C+B_DU#pDL z&VKp&5N|C1BzOTCc=UYUn@88_S@?3=0cBJeIqFum{c8^n{!8x`=`TeI{ENc~VTs1k zD>P{e1-=O{Z1n;_bXf{kAiyF2CNJdVd1+6RNP_XA#Qu8~vYS;6;wqqw_ z&-HIRQkLHg!|=*J0-kmU4^s%oh<2(We1~M=rZ1@1{P9F-2fT)9oguV_r`?If0zG}x zu6_?|MJKHiR;~?TXC!@a@l4W$s)$;s+YV{iyWDh=r?Ol`p;2qawj|gvlf%Rxfmyle z?T9&PsNz`w#Q9+55zU0lbWnmlb108JE5*!E{gt|1VfY1RUQvX9^gsf-s!KN?@r5fr zI=}xJtR%W$Sume=;bflouqDn^ezI;#ckZ8@(6$<*uyfDC>5K9R|*^x@WpQ&|O(7JYvJ z*?D!KbkoM+2-)Z6C71b1_w5_uGyB9Z*O-98he_TrBQLCgosun2s!LoENm?O%kyd+3Zwh1b z!F@yf(`~-V-BVrH9q*2VpC*QT-FFZU?@%o zIYsef@D^C#ORKN{YznFPNh2k0y}nfBVv(m}tAb_vfQ+$dn9p2Nc7-crb8MWu2wC;i zPR|BarBXDNC(BR;sD7@gWezb&r&<&Zqj|?cSQPEXQKfWGTuAf@*M*uHw&Gn|CpMHr zVVN}dNmy}-?Ue90qgtEDsduAn^W zhp0~r>|`295r)>L@V-##oL`=}tsO83q@04GMfwn;w&~~rV~B&+vX$%MJB_`9q9!kN z!y?0#cw)9*^o3;PC>)%af+gxAAO0CFyi&AAXiyLk)PF|Ie>t2R$n1pvb=?~~;{R*5 z_j4q*H^GMb9vi>Em*f6*k@ z=Pz#Ldi_P6JlDTCnHTsM)$`5%Vq3n|U!*KB`imh2)_?J`zySV_^;+g1Q#R;Jp#kzg zh5!5J2kI`=B>RVf`VdoS_s6ph{ij935dXQEtAO%~7(hlv6r}$M?}XsWi2exi{|G<- zrw&38lv||yH(6t}em3F%S2OVCPci|JTruX~i!&}JgjCl4L-B)JiZT9DV}Gb|L(rBX zA4t9w>2JFKoSQiR>y+K5e}t?c{}PP9!vB0Mg8wBPv;HHb0X5*RR910 delta 12805 zcmZv@18`g?K8>+0%t zUERA?UAgTgDxf`OxfKtn@=bm|Qz!W)1+7x&jYSbhb2DeiAv{u=n@ub~Gk z1Nzq}hsOXz{*xsI@*j^Ba7P^z*jvEXh>LvnuLAUcxT0zq9~S)xWgHc}f5%Sf<_}aMgeuctwW`AY*1QwKH~gO;Oc%L=i^g z2TT6$M%#q(CQ-2fTJ2C5pFknQM%>3yUo61UI00Tl*E!X&HuyP@X~_0s%hqLh3|lPlr^)^$lxi$$Tcp&XF_M>k{VhD;M}obqV%RI( zvCexs@f>kB->eq1G0}+9k+dS77^*I~ze8t`arbZDb^gOZwclD9UnO0}>y|n>4lsgu ztVF;~7kqlhM%cdgrc_mXVyDLM|JpMyF5IG&Z1s(X2Uwp|l2Z6AcG6Pqqz2w}MZzjm zIP4)XM^+xa!hK)um5Q=|$nH$EVo3(lX8Lw15dfN>2UfZkVk=cGmlcST#OM*jDUEGKNCaHhmN7K+4aj?lgnWP+6p4DHUd%(K@CijCSP4IL! zs59837e_HtUk;={v5Kf@RmSFAQk~Dfgq#eSbAM?pX1#%-3zpRJ!2@pgqgMON_{wAQ zor3ZAf+)#CL4SD-OGN?&0r>_F@+bZO4d#DF&;*?fKz(81p);I!f76!*Wt992N*L0G zd<2#}IVcJ`84iiL%1jh-yAT^$!WX5gmZ(N|p=yX`9Viugof8n6pZ0w=&5H%i?k#zu zIj2g#!rCvp3>>t1(#`o*R$@2Rohtn9Ly#P@a(YF6<3SJ#c4sG!yQ!%7`Q6V^Nvm( z9pHJ#DlY=YX!=4!S#^D7XD|#$s?N>z^7XM_qOz1-nTp)vegdo;RV2iwu&Wt zXJCN4tqJkhEB7WkoynzC1se*Y!>{0!Y|SlA&2I9AE#{_&Dr*BV{*bd?TP|&hdm^Kf z!r;pLiL0nF%W%+W5=cTyM3p_?>1Td*n+X@1wlv!~`1`7wo~{qaVf1jO5L2Ng;OIx| z0(e#?{c*B8*mfSOK3ay;g;I@NA#pk}>6ssjpOe10;PCRI;_I&$*SI>E+QMQFZ_R4w zS?P^-#K#e#BI|r=>yuge?5TMAZOm*cuvCl>`-Q7tr^FK5Yx#+T8)MYAW0yA-f2S8) z*dxITMfTfjOUlAW62{;94;?-NM;tqi+-}v=1&~PW=wEYL=)k13*(9V zE(5!ZI~yy#gbULrhjA3*x0zzLXJ?T8;@#od=u5n>9H1Fbtn6$R99q_!ItT1fFF1IE z%DF^{l4>xj5vi~7owiFIz`-&60D9o2NThwko@^}y8#CGY=JZ5<=R-4aD-%UzzeME? z(Cz#DIqqB2%b}}iUKbnFTP@#vqPZnf5(RuyIScyB#us4z@EvGOasnRpz%J+_nL~?{#`4pW{E;Jm9$%O} z9_>Pkjy#yW3T(pG(LOh$qO8$?ztz#c+xh)H^Ji0_k;!7_Vl+i25Re1&GEb1$lLo_sBD9N%<{HA53Wv{7eGNy8(w!UCs zm({Vhb!MFVxq|FQGW^|@L~Ceg$dWJgtNj^8e~pI+_L~MwYcEw0YrB70IjJx$OV$r! zCmrool6iM>IF^jUbbs+Nt3 z9sV0&?2Nj3!@Au9anI+C{@+mbL>-6gKF|(i`^2*6B-TC(f;fVr^l0X zdM!^Up*^*Q&XOgeyY72Y;{-yU{{GI+GE}iUJ z!=t#8HUD}lG(+8YDAG3g-a=|O6+Yrn)GguKz20CGY>E24DF6!}1(GF)xmsOzMLWMS z=AX5qx!WGu*Pi0kVO#x4AG~)&BXg@zY{}S6Bgol+@Y96GdNF=466Ax3iR!+Lv_7Fv zS9@({#>)m=aW_5$7N&NvxdUe{(`ra|!UL$*3iTWP#~kP@pmZgxd{t=ImZCy#U6|2+ zUv`;@0{dD74uE|*EYQHjX5=!FIDB|e^{fUvQ0J@b=p-9h(b$>dnz&dpOk_}K<>jUu z^kwsaW%(x>HpioI7l{(bn(yj*&zj0F6LtpaHr&A_1@vsf^wS*=9%EOantoHlFvFM3 zjh|)@YXenrV{}C&KPSr8?V?c2K{gA1CFm4oY}o75%K}iWGe!j=5NhS8T-+$n{Ye-G zCZfZ$LPB6u6_g@TArEgneG#g)=3reUV~nFHA= zL?V(ImrWnds2`tabxguO?Bv0rS+M?ieY&HtyM~}Z9ILm&~PW6 z{7Hlo!kc;d2jI*m!ap5!(;>{JkKU){d}~P&RA}OOM4dR?{_2q&nHFx0b7tgW=c->F z>Lur0J1ECVyih-hG2ZjJ&#Hq>WXKMcKK zZ{pWtgguR~9Oc4EUf?srMUQzvMdj$rbmvd?U_BtdF}7ffwr8AjdiT#b*GNN>^(i*r0-3qo`(E$Q@p0JkgJp-_Z6N% zkuqs$Jm*erG;59P<(D1l{Y*?%`T!~#b;-8Af{Js_R6A%|6216jWmy)z*$M4I5{3#s zxtS%WOqxqf@biXC#x*q$+A5kPMcBLr`sRlC4eGwi4{$I1K=2&#fgJhq8Vd7i z3i0W8L$!w=8_Dk>GV8uRlbO!=QEg3~lQgSH1=)k&(; zl17v+-iN$M2ptlm1>p;?O(H3lp%wflXP@Sw72Vo9JTKk3Y(0ThSFjbGiB(Rp$DO%V zzeHuKDQVYV@*4XelX}F}fB+(l`+@X~jy)V>`fPfxqqg@js>xZ&Z9>MG!xV* zSJtV0frjOGE)ZR&FNnKNd?7r1F_3&g++JvuJIpsyy+hPb_&oLSv;b;YIqjS9T+X*I zp|CB6aE(q>DQ21EfFqsR7MCs!gk~C6jE4iWGZAL&gb%^gkL&W!O?$0Oi-7@s+*BM~ zjYN2pa(qj_7Xo{&d%?z?yyHjhhMilsQcNoZz<2C!DULVXl`Hawoe8r6&C#&hBzQwo z+#UrEzhJv3ij^z+2EfkR=T}^XA>_rd8v+#JT%|hfl4@6aPcq#aCzBl=@F_l1cz&;v zAPa+{qD;n~vUoXB{zVfO%u2ayA=S!@J~mZL*vx?FMH0pe=~*!vb1{Fts28at8VpUH zdV&GD_%eg|`t`bx^fHG!u!sE7hy%;^w<_X$x)ooL=&ITWAfP|^<{i~HVE`Szq+bzT zIcGMM=g~+gRNJ()zv{Zly}O7eCj6@gRpo_JPia^?dhsdGlP?l8JNlZB;B##2uI^a$ zHx{<>Q>%sE{OxN;xc;;|J1B_sT5EL=6E=@T%cn4n9?ZE&v`KVOO6W*nNWTaS5+y8Q zY7`MqBmo^JF(8||zfDNYsobe9%q1!>(r%r^<%%{jnpceAqNHg@m>H=THTbHH>Icw- zDHkKuOBij((FEE6W5`Q2d0U?u?=l+sDdO%3y(rW$IkGMyk`5U&Sgak3hDPjbim6Gq z@VcByI5-%lvmOqDhqgD{FdU6E9C_?ADoy4b$AB>V20$?AQguvHfJlhNB?7a1J0&WX zaIr>jxGY6^cg%?o8)3|-DNfAer$R``eP-t`BjZzCZ6x;|5d{yf?1Gd)r=)9n@{M%+ zXVZ{4ZR%A^sVc7Y{^1a|)WfaV@6Y@voKUXaMD8(({A=(k1zN;oB~wmk!vQz(M9*`x zMD6Sj^3NINNh?f=_E`)D{7l=M7dmv3?rMSR7}OIo#9HIS*+haNZ+s-uBT&S zXqV7U)xi6`fe~+mp!cSWj(HXmYrCN(yp}i{eFz9EtGQMmEb4i!Q!2=OxNXLA)C;}RBR}=Sl_LVj9bu#Pu$x%(_r7zffO)_nE7tS?T`!^4m2?8l|BXMT%4!Ir0&5oZNg5Z+Xi?|&UZN>TW|j9WrlI*NfvF z!m&fv+*WobZ;7nD3VFi*AY0tkcBcQJsbQ_;o~j89A4{7#G&wMD%F>@AH!v2jd}xcL z$5D;Ij$`lifn*p~*}d~%Bs6MBNZSD1x6a~et|CUHicRX@rR%dX=C3gk z$cA2P(Zq}-8Fvj6(vlF@!<0Zl1}hUb;~O1;v_`#@$AJn@<`ZcTh*gd=LB<1=oW{=0 z3ea~@I2jZtcom9%fYj%$c_WoZg*%0LVf~JjbSm&dON$(-(|9B3Mbf19@-;#KiQjM#y43L8mY3M)ZN zNUpFlMPpaG5ekWJ;M$BU7j6W&qhuI14E^NmO!u}p4eM*1)KBIEht5*eUc6XQ+^Z=| z$2EAO)ty^LQGP;31oySP-dG^FnE1Q%A^apFMW(I{Q3D(=Q>*&DPf`tFk-r;pguXyK z9Kw0Ra&bqiai$%6ELO~)t^vw35zINPO7B>OuiP;=S6AeU8SkR1p7o@quV-9-AcjKtsa+&-!-NmT-mo7~=Is zc=t;;3S6-zqA zYlU`+!uUeDI_P_Za%md+l)yD3$tCF*6g|}I)c%yrw`2L#{)7kMjAr~r`VqS}zE$;W74WBM1G#vYuz(OU$B5F2~FbXtQrXnpA zvxrJ$k&Q`RDU?G%LT52ln+PnFKtKVStIbdJnWWO8jf|5x7vj-ij2aiJV?;A(lV~VC z+eP_MGc@yflZXNYKHM6|nw}CTzQ66l?VfWt3A0e)WDAqar_wN0jY|r6HW{l2C=2rq zE_#0~jnZH5-$)mnAA9lR*ILeg#z_V+dTZr%jzt8cuvZtGa4`+9M9R?|ym%0C(bZ0_ zeA!Y!Q<_4e-2aUl!`fZ_h{~zUMU$Fp`A(H#c|Tzm90UZo!|YftJj!>LppL&%G^*{P z&V3EP9e+ITo>1M)Vr_N_^J%yC_V5Pu_e_$W@k?nrQ{EiG*Q;F!xll73Z%wz#fhE{q zR8H>U-1yiMNoU;Vh9*`95o9qD6C}AZT(ApdH;gV&om*`MQ&B7zb z6jtdctXi`v5K=4u07JAJ5nh)9xx)5!!ysT{E0i5Ci-o%{NQC@pu`@4Q=Q1tztQ&V=$ZeDZjTs3UWO z*8n!>|F!?uEFPQ-@x98;juGbeUOnXI9*py{OjVwoD6$RP{PM{#cowh`3GMhR0l-`4 z-}0K>m=F7+N|_nTQsr=%+d_5zrP15UH1%--(MgB;BJWPdl10UUqg;>;q_)z z74VKRFB`hO-3L}aa`R~{*6=LSMQa5I_*WCLqiMau^$Ho~BVpV786rk z1}KQhk9%g%AgSX}b6)zCeYcijnVu%}Gakk=r)6aF$9!o^IaUVP)-30AX-Aq$I5`K` zJhl$09Uu`X9 zswz%l#fQ#bWRw!RN0Ef?6IxOrmhPFd;h7cuK+qTv;)k)f@A{PMM}T?&JKU3ctT60z zhZQZy(Sl}ne?2@qnKgnfOS`Ya^+}$>wqS<+deXGVlj>Ya8EkB$Tdz)Uw4lH>Xap+a zwCj%>aG-4#9NFKP!GYw2xK7_3SE|1L6WvB7=6qX)3w83%jD_=<9Zf;~b(cLL;KDQS zHDm`+6E^=4Ak_VSo%tidZ%aNoWggrYDHbw`-nS?)lV~e-5&Xtq)ZxT`giklR+#1n` z>Y({OiL`99ERSGKS&QZVHY*^*8gZOkAXPHBE26KODhe_~7b;yBiXL^8DU>EFsogi` zi32s_#_C(E@sDcQ^bP^;&e+yZGOR91gEKII&I=;5-Ulq{fhC%!;E2{UM8!cb^+kZf z+9j~jbU_@rz9#9;_r5oy$3$uq((W%1SreVGd znG#Bn61=9y;GpTfTn4#qkP=UCgG8VKu`J^bPz%29q8W^?fUYe^c}v5Idz^>%tC9fh z(z3&JSExy`>wv4vnN_r5m5qxh;?Raw${?%0M!wH~fvm}{LKf

jJyB&3aU|E{ZH8 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 HiSvH|-Lzk! diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 70bcb229a..91685acba 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -360,7 +360,7 @@ public abstract class RedBlackNode> implements Compara * @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.compare. + * order, as in N.compareTo. * @return The root of the resulting tree. */ public N insert(N newNode, boolean allowDuplicates, Comparator comparator) { @@ -829,10 +829,14 @@ public abstract class RedBlackNode> implements Compara * inserting all of the nodes in "last". */ public N concatenate(N last) { + if (parent != null) { + throw new IllegalArgumentException("The node is not the root of a tree"); + } + if (last.parent != null) { + throw new IllegalArgumentException("The node is not the root of a tree"); + } if (isLeaf()) { return last; - } else if (parent != null) { - throw new IllegalArgumentException("This is not the root of a tree"); } else if (last.isLeaf()) { @SuppressWarnings("unchecked") N nThis = (N)this; @@ -1112,6 +1116,7 @@ public abstract class RedBlackNode> implements Compara * 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. */ + @Override public int compareTo(N other) { if (isLeaf() || other.isLeaf()) { throw new IllegalArgumentException("One of the nodes is a leaf node"); @@ -1325,7 +1330,7 @@ public abstract class RedBlackNode> implements Compara * useful for debugging. RedBlackNode subclasses may want to override assertSubtreeIsValid() to call * assertOrderIsValid. * @param comparator A comparator indicating how the nodes should be ordered. If this is null, we use the nodes' - * natural order, as in N.compare. + * natural order, as in N.compareTo. */ public void assertOrderIsValid(Comparator comparator) { if (comparator == null) { diff --git a/src/com/github/btrekkie/sub_array_min/SubArrayMin.java b/src/com/github/btrekkie/sub_array_min/SubArrayMin.java index 874db6575..63634c9f8 100644 --- a/src/com/github/btrekkie/sub_array_min/SubArrayMin.java +++ b/src/com/github/btrekkie/sub_array_min/SubArrayMin.java @@ -57,6 +57,9 @@ public class SubArrayMin { int min = Math.min(lca.value, Math.min(start.value, end.value)); if (start != lca) { + if (start.right.min < min) { + min = start.right.min; + } for (SubArrayMinNode node = start; node.parent != lca; node = node.parent) { if (node.parent.left == node) { if (node.parent.value < min) { @@ -69,6 +72,9 @@ public class SubArrayMin { } } if (end != lca) { + if (end.left.min < min) { + min = end.left.min; + } for (SubArrayMinNode node = end; node.parent != lca; node = node.parent) { if (node.parent.right == node) { if (node.parent.value < min) { diff --git a/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java b/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java index d69657f7a..a29719ddc 100644 --- a/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java +++ b/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java @@ -13,16 +13,16 @@ public class SubArrayMinTest { SubArrayMin sam = new SubArrayMin(); sam.add(12); sam.add(42); - sam.add(16); sam.add(-3); - sam.add(8); + sam.add(16); sam.add(5); + sam.add(8); sam.add(4); assertEquals(-3, sam.min(0, 7)); - assertEquals(12, sam.min(0, 3)); + assertEquals(12, sam.min(0, 2)); assertEquals(-3, sam.min(2, 4)); assertEquals(12, sam.min(0, 1)); - assertEquals(5, sam.min(4, 6)); + assertEquals(5, sam.min(3, 6)); assertEquals(4, sam.min(4, 7)); sam = new SubArrayMin(); From 5e23bd9c81cbbc00ebd57b7abf70f4816e35d0f2 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Fri, 24 Jun 2016 14:46:37 -0700 Subject: [PATCH 14/24] Changed SubArrayMinTest to use Integer.bitCount This changes SubArrayMinTest to use the library method Integer.bitCount rather than a hand-rolled bit counting implementation. --- RedBlackNode.jar | Bin 43619 -> 43553 bytes .../sub_array_min/test/SubArrayMinTest.java | 7 +------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index f44200aa713b85fed2107694d8d5085cdf12b7d6..354cdced979fab8f1b508a86ee9e0ea98e4063e0 100644 GIT binary patch delta 1452 zcmaESg=ygxCf)#VW)=|!4h{~69}L$f^2RZLV7Rt1FP#%aZ@$6V$OvY{aCuo^vYtR_tB=#UE3e*pm-kHCES;+rx3t<@_)H zKkUaQoA-8E?Ebr;>f(-*=gt^^-e>&zobmhmcl#L&KYTg#?$+M3 zzIiM9maos;w$?UcbwO}_Dev*naGTnL-*t+Mr*7ylXpXt`E;B3Axce3F-V~9Twii0) zJFU+vq#XOi^UyW>u7`s|j1x;>+cp{V-q&%i=5IoM^`&{YDhRZ}}PSWtlr?oz|Va>9pl(y;Z9HQen-}O7*uS)&9k8 zS{cQpd8GED=0k~HFYey@Wfgip<@l$GhhLRHINtC5!rA_)CZn^; z1*&uRCEr-rGFc*R!WM?uov$-fkMG~M#icUv?~+}DE2ST@i1>6^9G+nHNG*Gp+rwmcAT1T}y{$;nD)#hws;8#CQap$9~*0ZT^ z{dyl9>3VvT^|97>$6x%v)c)QxWYLng(RLBb<@cO8!E4sf^>X)J-=qroP3U1eXA$-1 zWC;HRze#BsOEWlfw+0(!dL6lUiBs#qr1cYb?d|THqvXAQLfDg!4st9(-%@o9m^CMP zERL=hOxaW=|2|QnSJ1V?#9_P8PhFwBhCo#ZhIF9f8O%rdi{mg=of8`auy}81a*H4e@T{R zu5hbr&)ciu_C0g${X_|QgC_qePp#c%wp?b+Yz{TbT5>dkyilWqtBRrJ+EgB@Mh0ml+aXgsbu{Z-vD=&!!)6Gkg z!1RkH31HfLX(pK7zBC(5t1g4klb1on|1X26Pgx$v2l6l?C?;Q69s|~*ze1XcC1>(Q z0WqL=geG_8a80gT!2_1+1xmT(L#0d=Kn8SgKKI0mgMqEM6+B=KoOMl#i@6yXdSw|HbWk*K*1|3AuVv#8@MdHZVSp67pzuAoQksda6RHFh T%#KV~=1z88CBwFE9>^R3!5wo* delta 1498 zcmY+Edo+}J7{_1pQgbnGF|O0nP-v9$25MHcOWgvW-JTsHV##-DqO;hQYdy z!5B2{kctUqlcl6pE=7mwv~s9Pwq{Lf&2OAj`~LHJ-sgF~zvunE=RDuCew3^qg{K9= znuZV>jfNWQ`?B#2xTe1ETHJn&ijqN0ss;#l;{s415aJ22{zAzQ`sVRN#&#GZdZl&} zJ$8qrgIT!G_Ib(YqS+imk^L%WOK&#*@8csN|Ha6M!G7F}p^xIT}}-gS6O=aW^^ zPrSGJ;wifbt#FOx(Gb_!^6=ityqWjEwi{jeO&r_(0PXiWH9A#3G{Z4XJHxbaUV*Cf z>~>$v610ToJI`zmvdmv?%o%T>UpwA1rrmrfm*iNWY<-idG`fLJVtj}lu+RT1H}cvH z??B$3yN!Rki$pgQqk>C54zk6|_u2Vjrlol+buJ0*6|?kW5D8;6TbHa)^o|&NuvcdEXsWfu5Clt{#S?$G| zj+k@1UG6+K%5eQDd#Byy(*9uu!{v5A=c=2`T$#wZd}>}7j=w?qo6IK9ORP`!*|J`> z7dTlzn&Q1N?WXKn38Ng4dU^=YF)q&0qL6$n=k(Fh8S13&{0F5jPP%tjyL4lN;&`>u ziXxHxDn((ir8n&$XPtoupZVd(ppmx5v!b?@zMGgMgM!4UGFlAHFhYSh7+{n;R0-c1 z`$bRgEgeAB&E2wdWW#kd!J{&N`?`ANRk6d_Jq6YS)blI95HJJORsx-reC6pCR{Bo( zao2v2=OaW$fXq@iFEU@=gs)7(;;TrjVVJ>E~~Bjintpn zp<$QYmtQX-#)0nGGx=^X)jW(Q}}o@%F;)f+O1i*!PBCNQ|Vhho+oF$=zw2&kDr(GOt*FYi<=#D zIPo_Bv&Lt|1&V^>`qattXYC9IGXi}KE^yf~=gy2XF!MclM$@|k!x^`=WA|V?nf|pS z>9U49UexeU&bD3NU9~0~V~$i!7(p~(vLFUf zwn%!VIxnqM_phj8=%~!?>y?qrs@*^`U1g5)jYazO2n*M-1*yPlbJV%`#gqZUttMHj z#1|zGU#>$#kc$BXQNM9X2r5tdrWu3OB^j)y1!|xSA+-Uk#F&$(t@t>> 1) & 0x55555555); - int value2 = (value1 & 0x33333333) + ((value1 >>> 2) & 0x33333333); - int setBitCount = (((value2 + (value2 >>> 4)) & 0x0f0f0f0f) * 0x01010101) >>> 24; - - sam.add(-setBitCount); + sam.add(-Integer.bitCount(i)); } assertEquals(0, sam.min(0, 1)); assertEquals(-4, sam.min(0, 30)); From 9534c4ae06ced06f0cee7933c7c1e906372b4350 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Fri, 22 Jul 2016 17:03:36 -0700 Subject: [PATCH 15/24] Fixed RedBlackNode.concatenate on two one-node trees This fixes RedBlackNode.concatenate to work on two one-node trees. The check for determining which tree had the greater red-black height was incorrect in that case. --- RedBlackNode.jar | Bin 43553 -> 43647 bytes .../btrekkie/red_black_node/RedBlackNode.java | 12 +++++++----- .../btrekkie/tree_list/test/TreeListTest.java | 5 +++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index 354cdced979fab8f1b508a86ee9e0ea98e4063e0..f866453bbafcf5ccbf073e55cd95a7fe2c359065 100644 GIT binary patch delta 14834 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 Mc#v>;|5fJy0IPK0%>V!Z delta 14730 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 bK!^QDnHYpyCJeri3DPaYhpx){ANl_SEz__Q diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 91685acba..73a8dc535 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -772,9 +772,10 @@ public abstract class RedBlackNode> implements Compara N parent; if (firstBlackHeight <= lastBlackHeight) { parent = null; - while (lastBlackHeight > firstBlackHeight) { + int blackHeight = lastBlackHeight; + while (blackHeight > firstBlackHeight) { if (!lastChild.isRed) { - lastBlackHeight--; + blackHeight--; } parent = lastChild; lastChild = lastChild.left; @@ -785,9 +786,10 @@ public abstract class RedBlackNode> implements Compara } } else { parent = null; - while (firstBlackHeight > lastBlackHeight) { + int blackHeight = firstBlackHeight; + while (blackHeight > lastBlackHeight) { if (!firstChild.isRed) { - firstBlackHeight--; + blackHeight--; } parent = firstChild; firstChild = firstChild.right; @@ -802,7 +804,7 @@ public abstract class RedBlackNode> implements Compara pivot.isRed = true; pivot.parent = parent; if (parent != null) { - if (parent.left == lastChild) { + if (firstBlackHeight < lastBlackHeight) { parent.left = pivot; } else { parent.right = pivot; diff --git a/src/com/github/btrekkie/tree_list/test/TreeListTest.java b/src/com/github/btrekkie/tree_list/test/TreeListTest.java index f59a86f0b..c1e335c85 100644 --- a/src/com/github/btrekkie/tree_list/test/TreeListTest.java +++ b/src/com/github/btrekkie/tree_list/test/TreeListTest.java @@ -72,6 +72,11 @@ public class TreeListTest { assertEquals(6, list.get(2).intValue()); assertEquals(null, list.get(5)); + list = new TreeList(); + list.add(7); + list.addAll(Collections.singleton(37)); + assertEquals(Arrays.asList(7, 37), list); + list = new TreeList(); for (int i = 0; i < 200; i++) { list.add(i + 300); From 3c75a5e39e00d624b8b1f4783baccbb673bb7eb8 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Fri, 22 Jul 2016 17:13:19 -0700 Subject: [PATCH 16/24] Moved TreeList.addAll test to TreeListTest.testAddAll This moves some code that tests TreeList.addAll from TreeListTest.testAdd to TreeListTest.testAddAll --- RedBlackNode.jar | Bin 43647 -> 43642 bytes .../btrekkie/tree_list/test/TreeListTest.java | 10 +++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index f866453bbafcf5ccbf073e55cd95a7fe2c359065..fac54d1902e8b66053800b546dac65edc9d1f834 100644 GIT binary patch delta 5864 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 diff --git a/src/com/github/btrekkie/tree_list/test/TreeListTest.java b/src/com/github/btrekkie/tree_list/test/TreeListTest.java index c1e335c85..e01a63810 100644 --- a/src/com/github/btrekkie/tree_list/test/TreeListTest.java +++ b/src/com/github/btrekkie/tree_list/test/TreeListTest.java @@ -72,11 +72,6 @@ public class TreeListTest { assertEquals(6, list.get(2).intValue()); assertEquals(null, list.get(5)); - list = new TreeList(); - list.add(7); - list.addAll(Collections.singleton(37)); - assertEquals(Arrays.asList(7, 37), list); - list = new TreeList(); for (int i = 0; i < 200; i++) { list.add(i + 300); @@ -225,6 +220,11 @@ public class TreeListTest { assertEquals(15, list.get(0).intValue()); assertEquals(5, list.get(11).intValue()); + list = new TreeList(); + list.add(7); + list.addAll(Collections.singleton(37)); + assertEquals(Arrays.asList(7, 37), list); + list = new TreeList(); List list2 = new ArrayList(400); for (int i = 0; i < 200; i++) { From 6d6b968fd644324d864feccf8f2a3d26b43ef119 Mon Sep 17 00:00:00 2001 From: Bill Jacobs Date: Sat, 13 May 2017 18:19:03 -0500 Subject: [PATCH 17/24] Simplified if statements This simplifies this: if (a) { c; } if (b) { c; } to this: if (a || b) { c; } --- RedBlackNode.jar | Bin 43642 -> 43628 bytes .../btrekkie/red_black_node/RedBlackNode.java | 23 +++++++----------- .../github/btrekkie/tree_list/TreeList.java | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/RedBlackNode.jar b/RedBlackNode.jar index fac54d1902e8b66053800b546dac65edc9d1f834..234edde7aba36f98c25c31731a07f01ef59654cd 100644 GIT binary patch delta 10066 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 delta 10097 zcmZ9S1yCDdyS8zM;_mJgcPLWa-6>Wa8r%cLgA?4{JvbDKTXB~{3GT(Ev_0)d=ltjU zGdnxi-0vg%Y~Ey&o%?hF?r8xIOG6nR0Urhh1qJ49^qoQymKkhxqcbcltTV(FwiXW7 z8BBq24eJTPK^#Ot5d0GXSw#(oeIZkHMYtEL!X$uyp#f}#f7&ZNU&I$Jmbm;S7kq9< z4{jhPg?LiP|BL!e)%7nENT>C$!y;4A%PRQ7j2`@*oeYx3%JWb8!Y&U)_R-N@`+boj z`m+poG!X$8h2{&rY^)rrjg&@I#ONHp#}Ew(H7TwN684Ys_7!wJ12)!GU6011A_F3J zN5gfahB{MyZvwKRfD7S+sa!?o-@QRwkm>8_j{6Pm&Fj4T=S{EkT-%_a4|Kr}dV7er z6%J0u8vOOb+`!qKD&c70-NWUG;7Ik_jw|B+H8 zVq)YocK;|ttGq)~&};m#Z{Z0vke&|y%?7)^9G@S2bc;Dpt-}Tru?GGSzok@IlwCZs z=*LM4r7vGa*iDJOfpSaFS{AYNip&5%tC)R`nX8i9WSAx^3X03Suw$)dZn~v8m5-Nv z-U}mE?gSU1GNW&z?tl&-QB*-39{cHM&d##ModKiuD+JX6D1hQo{RnKG-O^(@b`Coi zOpV(HY$S^UzxdpW&NCwEvSUIMg|MBt%lP zwyCmmfLV==(~s`A4%^82E5zm*b}o*$AqFIT&OzM9E;(Bpb2=Lv zn&fOQG>QlxsO2i-7&3NK%V|6viavFCZbs4Y;xAiC6Bo4KwGF4Mc)f9KQJW_t;d@rq zTnH4(C~Q6$1fms-AJ`lUwmlAZ>b1xWUgFU2wZ4r;y_EgLcssJIDFaU9dQe@mGuj#FNoRI=IsO|lYoSuVTYIA@cy#cchOHOku0|>8q zU5O%QeIM7wuC6`VPAtq&h&9UB?#?o3uHn9btREm`R`_N)5ebWPYZ2?hh_^&-vVl76 z$YHuW40w-J+nd>#me$I!L&V`^??T_~-QG2`QTVR^m>BK8Rf0#JLfI0XV5*$a(|EF( zIgFHFyFt7s5rI$;iM_41)LyBoXv*L==e*^1?dJYaWJu_v*;hxfNVd$kyfv}N=l<@? z%e3~8_O^iq?Api1q07c$&~SEg=vCLEn!f&EUk&92$Xoc`Nx+0=s;8g#{=F*uu)y(LEf!tN3guXzTE1(H3k*=CCQr{^Vn{F2-bk!d#ZS*E$D8vNN|HrqPfrhJQn0dgI}8z0BV4*a z07&9|ST`Wu4yVuBizlfGOJlD*C#BXW1)0zGJ5{sf>nVPuXZZdI2(9Sx-*1Rq8EHu} zcFLLo z>rXiCd#N8GQCW8PV1oEq)p&N+SV|+7KxBtE#-bgT7S$qvt2RFFU8vJW20j?tBEI{n zTfMBze?H<+1d~mR)$zK_MJf(b0?fx?F6ekwHWsWeGs6;TsTxCY5J~(UDR;y#&dbe} zBF0DuX?~9QdYj^^FtTl!fCm@LK}3tDIp}U%JmSjU#Zh~m(^x8-z!=+mVcek@EmimM zhYuvDAV{HUDl6KG$ti28O!}KS7DR*qj%{I~!#4vQT6r@OGd)&Va%VSQ1YDB6{^(=$ z>xyCEp|MgI>zq19YI`ae1ceoe(?rAz(@-oWVU=dhTPGJ3mUDNsGWHie^M}c|z&F$! z=-06_q)@jA5p^n-&8nFUO1Tq+BpeiMDJaO=+Z+w~dC@Rq|~ zZ=8>zNWt_r2B1h5$IK8rI;;)M7)lHi)1n0{H4p73;BqU8pcLcJR(-CCUU8{aflWhQ z`_ahi%DZnB^u@!4hovaNu$jgI5}Ad`$w^Xr7Mucv*i+1-{;@M!}B~^OxP4w>pP^Lpw%2~rKD=5Ptxo$ z@A~}kdgqhlgJD#fKe6X~Fb$0i8jJ)$89qN%Xs4KU$ut)kR3J!M0OF4dAgcEdJG)n3 z4?KQ{G+0E8!`-LR2m@*QF{s!qh%=h(nitnfS}IeSQ-~*B46F1+Uk`!|B=>OfxKsB* zK5QJk-H|q|j87M-sEbx@Li+YJF=5|vBp7#uTbwn!c(#wbnMDJ0Wfm18e0wG6zd9yo zc~u-D1Q&KLE%|ia`CnogN~g)qR8=g@`UCa@Ga0_@Xdtk)GG%s07&78c*iJU8=Hc)!53aXKb%>bgU7;g$xwBtEHS&`D zDu))7riLYvVJgV9xJCYewk%v#7Z@awT{+R+c7yd$y+Bfq9(0cLIaW_YveNvcdM0DY zyh5X?0(bfXbvm$i49mE0RGfJoL%DA8d5o_p&R#aO3ma2Jc7mTxg(3t~Dr`l-7;h1h zqu8R<(~$PhtBcsYT&NKDFm0$I2~x+pu%lX=FrtzcDa)90j+ty zB@~6DY>|t`I!^(@MI)iEwA0c%;F!pGJmv_D)Spk=P3>3zjinH|8VH1iEP_^##b5i2 zm`+nm+s=ASM6E)iS8WCluYSLMf4>#J3e;4L%fsikGzpj1UE>(QK@7jSCH8QVj_91N zIOH%juL4>+*yC8jvD#?g*>sD16UJ*!lG3!cQfZKkHTi*`ZsIJbUqxA=^S(q2`i`z?7$queLIlSHw8*#T8Xq;s zU^9s|O9`dy+>C*%Dfiq|XY);@Z-xD_O%ppP7dt{%F;Hyx*(xJ`1i+s6IY3Nt>5!}B z5~!Y3CYj4Prfur@AnHb>6ojKPB&7qjPZnEDDmk+0`s8%1TgTvJ^TWF+13}^Ut5V5Z zuXoct?8@M6ANdFyqliWNPz9x(X4S1)jOyREJOR;lTwo0IQvOeJjy}11H%MdORg<>~ zlegFD2fP&`E|M{}=tz3hd7gu`PcK^MbXmalC`0LJNcZy%zn6qZn}T zL%!H_g_?CW2+4H25Y(V@DIF?0Tdj4`Xj*yi1Ffo)N?q%JU=6ou%l9jb7fu~KVf7Kg znd48*?)v3f-nElG3WC{6hH0Ct1FBE$Am8*QEVSm5f5BQy%_UaJT-NZg5JN-&GY@3- z3B|vM8%cK0m|!O#5i?LIltM;-#|=b>dJy(^d>I~Mhi5Zjtb4!O9=`WYAT37JfNNvG zZ^uH3BG3BK2NZtE&i47b2D`qAw!81i0-dc7*$GC%2L2L}t(4pn*(PZ3%Roc}bxf0r zUf)xL5~w9oE5NLe*2z4sodur)7lVATat?+WoT_}SQidxp2~{$bmU3LHu>xu+PA5Ry zbecx7e4^z9hThh9mRdSTmdfZ`EX*(i>{mE3v+bCh(`ecJk>S1=Oy=6XZfH@u9=KB< zokBzUsbz=4MAst|B}m}xOB)tnS=peD+~`E#5vOUdnPr>KJh>AQG|xTYsU>f>Hc!q( z5QUHJtQA4Gd|M!ybhum^jt&edL8!E*&&Xm)6wp@p_Njn?g3X4`-)G73s;V0+zDrL< z*bs6hjCVVtsy&)^qotoVJv62|Q~bfK;N`K#nyi(4b0eA?i0|#l23U`umAZ0jvL1W1 zTx-A(h(I)^m0N7K$ShWMtL4))0_fNq^RcJyXe}QzYs(pAFL|bx!~;{z`1r$YYIR94 zjbFX1dxLp&%}-pX`myn;)4l5DMTh%Z4RRekpVz{enT?aI zq>8mzbZ?AIkV>Rr1pQf&VAb z7ilKeXolzkTjGf~f~8Y)q>zkgbhUEX=C0zwqujxs$*jMVVmI;=$6Vf0cEx>m6Gdtg z#kK*;@f%0tr1Vk_p3|lS1VeTx$rV>b)5N3XT4AqOWeh%3GA23=YTcozStVEs`(qq5 zjsQ^o;n-FgH%0}(6Fz9(5Zh5xu^83U4!$>HdocaLB5apNB-voNZU$29OrPL>Y@7Y- z4}xh9Z}7i)K}}$bZ~&ON@KLiKf(uF}4*M|Dvwv<;tMY)C{R*F+lohp@sSqxu)j?)Q zCG}gxS(%DNoGF@CHMKSW#?Qf_95D+MnwHHKig41%G%drE@?Z-EL!BW^Rte%#DwdGZ zh1n2bP)%KWu}$3RsUN)qT$XMI9{=SRR`N;$Q%}0i!iG`>FQ^L-wbyKw4tX)-eVBN* zZpsInm10W*N6g#%45#7x>f`GYXfwnyf?LgTW;wT!ud0YNchRFLLG%-aZb+h{7av|0 zi4_lKw3(*hrq)SuJJa#7sYCdCH988YpWg~_Eo_Wssq&b5@)sim|5rBkvbHplmfX>K zPZ8{K57vRD{W`9%22n*BjUIuRIX`m)#!kLB2!6A5#rfP;Am~pPeoB$j;EiK(VL^2# zVc7rbb8SgI~dA%^#YW2U=FzjNy@ZF{;ffF^)mA4aNu5irdAr@du~X zhV6Bd=_>;$bPcWDkxwed2UuCKxIOEBE_x`NbRL(?#5R9TY8B$FSGzF_R1x z;uGp=avxGyUc5m{b*pjyeml+4*4DQc5>Um`1W)Pt5ztxfMbg#8(DoSoBI)E}?IzR>;ETB4ddEi< zI`CkivX5-RN)>tZjQwR74|@lXJAb(_lnMlXv=EA0-noDNkTcC>g_!?QH-Ob;cm4x! zs_TKgrFedHQ$Ih?Vx%lls?nd) zQ`+m^j789871IUKg1T9D0drP3nOY~J&;IcLhYXxd4qTJ^77q=_l z4v+f@=kKSogNG+rVJRPx#3VmH*$SVrqKmZI9?Di@i&SMdi4rhS%#6!`3};Q5D$88G za~H;yIZmQxd^MSn%!%rcQHuDMaN3d|MlJ<7zhmHWW6e_b0as_`3EtOM+{iM153Ov;@Qs*~Z=Q3c0J zji4slQ|;`d^{@a-8~&kX2|UaP&%(75oA^A6ext!Z2r};(kKG4F7C!qge5V?{50YnX$HaT|;`>gK(dV7K^) zN56E}_jfpZt;*rA%26cKqG8fvu&0Ow^fbOM(&7n5v2U<90kTJjD2j;A@qOFnZ-)J5 zkrSc<#5kWU!OWlCb?$*6=6=H2R~OO09%NIX%G@YhQ9RGoNLJ;{w$d0DlsFbNnqzK* z8cX^*jh5W8snLO}8k&=Lc*u5#W+f`32Cs-3ll$LJ?s+@`O&aQ+#e<0u=goB-YOv7ju`%t^-8VCiUo@Y)Y*!JGjrA>B);&@OT_qJ3OV@ zrZ`hZBKC$CM;V*0Rsua41JR|+o{+lnuw3+h8In+%uar|nsT-I4=$ANC2xtk?)z&*X z89ZK?NKgUZ=gOCp)Z_kWp2A9vZDxfsk}t?(^fj2P?rzEAe3T^DiD1tVi^D+Hmd~xK z$YQHlEmz5&Mr8|7tIcFp&_);v%uw=Ge(fs(YU_u!wYM2>J}LFhDfPXX6KgRW*%eV{ zQ|73xIhfPRomcT{QbLt1NGH?~$iQec$`T~kP(cQ6?5w^yCe|a+bw}hcH*_Q5&NSlH z4K70wQbreYiG7_)WTa@gW@b1J(M|v45`{K#XV~E`K8sZepZi9rM3&peRT-7V?6!8z zg1unan&lVE;I&Y>4C%SKaH@pNTDignA`H#F-kFoFM% zq%#iiP6czF=d`%m(cl4wCjtA++CXHqfnBQ3Kxuf79)BjVK?soms>8!-`!45!O9kH+ z`WisUsO?*35MMkboWU_QL=K=7?!D$VHwoSu3b!L5+C~6yPZ6fSD{aSr6*bYIX%f;K z#dcZKj>Z*JeHmd8-8RG;z+6WY=|v$7IcWr%ZoE-Dl_SKiHf(K=4j-DVQP}8Lddoz{ z{%eqRsCIYM#cE*4NI-6*4~qc!ett+qz<6V5AJk=7-5$w-d*7%%=;syDp8wNKY;+2b zY^4-RfPhKLyq0)529SyuHU7gsyzulG0UK~M}teOdc2FP zF=${=*h4TC!>>LoC>38e|YwVxjK zNWQIGYrH1GHS4@jBPr6@kHXDlLmD$HGTDzVY-(FEzr$(aF^^3R9FjGp#!3&j>}1E4 zp9r$V`l_BdWEss)FtJMw$YaM#AMIO9s*y|M*;AC4E=_Ny8)I6x6RS{7bDArqB+%bmka2 z-S#^0JJQfBP0en&4_HY67`v~p>8-$C9QDYNHMxZ4R0ulY&u8Gw9}gip*V(l6X)?`o zN@rT%8}ssg!E7Ex-d><8IkVtOgx#H*(y{L6#5VX1Yv3SQk#UXV3~9!n3j-e5@hxE^q<8(pKyQfH;z z^w#19YvQ<5re1YtcLiY=7f(%i8r#5ETGiC1aLPK?_7`O{$5a7nMBj^+f0__8_08+g z?(8{}Et%oV(CjQ!5VU4O6-FM^F_4K+bom%L$2B?M1!5BHw`TxIorhuwV?4S6Z&LY* zsb77j?tGOx(3ns&^g$So?npv2zR$vqope_!gLTBBj6A{WXp{YwR7_5=moSd zku2iA=#aY9!dC<-Yo)J)nY|5m9$G*J9c^244S} zn6$lx+8d!pgs6W`Qip`gzgNlhTj0U#^PIP6sF6hf2&W!UV%DHzR=9g|3r~-BmcL=p z=;Id`TK-#FskbPDA^|w8J`8QL5yJCASKI&$X3A?us$>WmaWU~zV zyuIC-x5;e{nlzn&V z*&FqSv<4tgM`S%!4a_K5wxI>`Jm-}pZA7bh-e0^i=;M?8yo;sVg8Av^mn$T6UFoaJ zABH2ez`)O_7y{;7`_;T-lcNIpa7eTwKE`8T zVxs%H0=T(_&d^P-+QWk^x7h;D9Hx4k_ftfe!q(}I_oP-C#l^YjnVKkna~4d#4hX1z ztXK)Yc&}GC^6J$)vJqD6idGY(MZ5OAZ^-U2g;*N=vB%@$qUZ#gFskH1xW_f_2( zqAS>zC<{1^vc|b1jk|aZM7S7rjo1s;i|N3qj+->*J@!+&t4H4p@uVic3YmyZVh}7! z4?wVZWJVev(PYi19`eDp+I_E*sJ_ko$hSXk3rK!uBiTCu#0S6w06kHR6-{S+&sm3`TOnTy6cSTnix0FLj)=fh{nNrbJOs-GCu~$gWJSsA^euNtJ zNt1qA-nJ{hq4DE29(Z!MZj7b0%~?0ysj0B(Tu1>3&?#>uF-(!zQhwaWmq11geKNem zQdsJhY=1(Gg5S*kd{g+QcQlZF{T<_U00>yI>GgIQVuI`7qEzvx*dtBJGm6wBH|ibU z?wGn_Lw?fjBkfuC=b(7%rgtYcni=rT#JOnHE`##_N+YAV!w5 z6!6X2yr5udFQ$D`=ZTn-(AVk~Ht;9B?&9dnWVYOVokTPGBISW=`6-nTJE;nVyuco^ zrW@oGH@4DaYqi~b>mVRC!!MlBF*TjBTw=y94b^NtD{hqrp)vzpUJi75uyU+s;0D?^ z1hy0}l(}=1YLJ%)rp4RbmiXC+?Fz%pb-sL7k@l3!)|SE;I8KD<{zy?W&~ zHf_CcOy?CuCnqk?^ch_~^$8{I8pazp^q%iR+K?1EG zMbi_txIVQZC}(0e${aEwMyhxx}~2IGR_>%oPY3MFv^@lPcLtT~b;r@4E@EzZ^55n>$n*>c|M@PN#3 zxWcb)#2#EVl#x-mGO)>Q=wM)C6=7ihpV{cIl?V(l5Q6k*aF3I3kX|B}p70C<7C0^b)PEA)PWv_iKRz$glSfy5%q z7q}~W{{rud&0e6t*!BfDN=#lLuEYZK??C^1Q^7w<^r-$S@UZbEPH-?V2#7E+68~ph z7?=p~hf>v-^uAJ3I5TZ9pp+F%t%n09F2j4#xynf44t4+O#NfCxj2Aukj}9>Sr(=V! z%W$dx_Og!aw~m8{fl)<*ff4+#UKeKIS!_aZK{*MS-5d>UR{pP$OF1drw&lMywwL15 zf3$PEe;O8;+MX2rScdVR2KRzmanL`*`288?^?x;Bbox)i-?uNYg781V=QOkjjR-I> zo2W1_;{O%=-t~Wjb6knoHI!lD@L^ueZhyy!flTd+puoWJ;=;g4{a4~gkN=bScau=Q zTr!}N6pk(M-x@Kv>(9T(zlFdPe{|;PfBN4|L;q*Luvd}8a)1@9D3Flz|5pDWM4K|` diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/com/github/btrekkie/red_black_node/RedBlackNode.java index 73a8dc535..671c35b6b 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -669,16 +669,17 @@ public abstract class RedBlackNode> implements Compara } /** - * Returns the root of a perfectly height-balanced subtree containing the next "size" nodes from "iterator", 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 subtree containing the next "size" (non-leaf) nodes from + * "iterator", 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. * @param iterator The nodes. * @param size The number of nodes. * @param height The "height" of the subtree's root node above the deepest leaf in the tree that contains it. Since * insertion fixup is slow if there are too many red nodes and deleteion fixup is slow if there are too few red * nodes, we compromise and have red nodes at every fourth level. We color a node red iff its "height" is equal * to 1 mod 4. + * @param leaf The leaf node. * @return The root of the subtree. */ private static > N createTree( @@ -740,8 +741,8 @@ public abstract class RedBlackNode> implements Compara // If the black height of "first", where first = this, is less than or equal to that of "last", starting at the // root of "last", we keep going left until we reach a black node whose black height is equal to that of // "first". Then, we make "pivot" the parent of that node and of "first", coloring it red, and perform - // insertion fixup on the pivot. If the black height of "first" is greater than that of "last", we do the mirror - // image of the above. + // insertion fixup on the pivot. If the black height of "first" is greater than that of "last", we do the + // mirror image of the above. if (parent != null) { throw new IllegalArgumentException("This is not the root of a tree"); @@ -831,10 +832,7 @@ public abstract class RedBlackNode> implements Compara * inserting all of the nodes in "last". */ public N concatenate(N last) { - if (parent != null) { - throw new IllegalArgumentException("The node is not the root of a tree"); - } - if (last.parent != null) { + if (parent != null || last.parent != null) { throw new IllegalArgumentException("The node is not the root of a tree"); } if (isLeaf()) { @@ -1221,10 +1219,7 @@ public abstract class RedBlackNode> implements Compara } else { int childBlackHeight; if (isRed) { - if (!left.isLeaf() && left.isRed) { - throw new RuntimeException("A red node has a red child"); - } - if (!right.isLeaf() && right.isRed) { + if ((!left.isLeaf() && left.isRed) || (!right.isLeaf() && right.isRed)) { throw new RuntimeException("A red node has a red child"); } childBlackHeight = blackHeight; diff --git a/src/com/github/btrekkie/tree_list/TreeList.java b/src/com/github/btrekkie/tree_list/TreeList.java index ba96c716f..41e411d91 100644 --- a/src/com/github/btrekkie/tree_list/TreeList.java +++ b/src/com/github/btrekkie/tree_list/TreeList.java @@ -398,7 +398,7 @@ public class TreeList extends AbstractList { } // Create the new node - TreeListNode newNode = new TreeListNode(value);; + TreeListNode newNode = new TreeListNode(value); newNode.left = leaf; newNode.right = leaf; newNode.isRed = true; From 148e9247d3370ef7a0d630d7300a2125e7261fcd Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Mon, 4 Mar 2019 17:01:46 -0500 Subject: [PATCH 18/24] 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 zcmZWw1yoy2x5bLPyA+2|T#6Qoy9NkW+=~>a5TLk2aCdk2wz#{yyHhAohzJD;Hc^a0*Mp<^{cZOQ9Z+irT@C{s z03n0B1iJ;9C_??m3+csHl@JwIka%NjQm&=PxxEDK;os>&t$ZIbim%ZvFC24H+L8fxk)s z@=BQaZmg7QUG^)L&qfx6oO?2&#uBBf#_UH)p}Xe4IuKg%vqqUt+T8z!6clQv1Yh9q zMq`IDOI>#Ev~OKbyvS6nV-P!TUown7z>s$uqdmbypPF^nKdawDt(yc+;5VGko_j5g z?CBml^8FTCv)`R^BAs7Wgb&aFS}g%t;+^v8&M$d4(&L>k=)_7+6PIq}7^_DPMzSlO zYfIS)>k%NBV@$s4QO?CJR3MrQ(o7$cG^ zTGx@1P>0GrlXa`YU$}_qt16Ml7=nSL6=hk7DGOgBk=x!p8 zB=<;;E_GdDK)&F&sX_ays&-J&Y~T1@bKAO?fDTGJva^E@d-MD-qwfc7xv03EE1l#( z$z$H#+112X{e&5e6FE+pj}$#$EM8OeUvCa_#+8*>1X+PuCF^qO6ydO(rEd+%ptZ}c znr|V9oz12+S>J5@gu{|=_wLVEJeyCPf6NZHtet!l8r=)U>u4yDYJIhb%zC>^6&vJV zm_5vsmY}32^KJSEb4rPK=IGcn!`oNe;^Ier^49WN9~9a0 zV{k8ZWH#bEbTIA)8geL-1!dF*p+DfKPYHYFc0Of*D=6Rdk=CvTs%WbFQTBz-51jzH zjFMS^V@|id)F@o(X2Tf+C_(-us2H<YqiaT@0Zo%R#kpy%&I;#=d?kt6R=$%_W1yh7`^ff^5d-DR-8A>nGLRXMlE*p z2OUTR*bmy?Y_>WIBn$gi;@*dmzdIUroP&3hZF>ZcFEky%lREO1olk|mkI#2v zNoX%guXn7^!RhH4ShRrRJJT~?;phT9gC(ZsA@9Mj}n)0MOuYoeID)# z0Mo6S%PzUDtjL@yt~ezT9LI$a!6Iz6Xsw=Gn0l1Yy|1mEs7i|pszx*NR1C`1V98WS zWVkpc(%YHJfP$Hys586Q&x4HgP;Our3DZbYlPP*TUAWWVL!mCW4MW%m!J)|Q4+3vhf!#n`~*&P%tGfg>ii(ZFf-MyK>$V_Wo5!;cKa7q@=z4b>H9b$|k-ql?pBu~ITgt)95VvGDQuB*Fn~ zg`OqQAap%lFM@&M2Tx_o`iw2oUz~{jsH+lA?)xOnYZ){!CO4*6)9B#O%Un5=hxM-H#xVwa(+60;dR}Ms@zvjnU7QFjrj1S~tnIP= z%;Qnv`5s;Q=4X#%Ob#>su=(tbb3a1C)hE#(Bj8O$r|*@leynY~BkXgnR?E155>e-B%md@3aG;~C-2cB<
{`W0j!c!TzzjFs1)u7&z{){$SyRg>e5^kW&j9 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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()) { From 8cf1eb9230dc243feb090352312c298225003d03 Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Wed, 6 Mar 2019 15:20:01 -0500 Subject: [PATCH 19/24] Converted to Maven project This changes RedBlackNode into a Maven project, by adding pom.xml and changing the directory structure. This should make it easier for other projects to include RedBlackNode as a dependency. --- .classpath | 27 +++++++++++++++-- .gitignore | 4 ++- .project | 8 ++++- .settings/org.eclipse.jdt.core.prefs | 8 +++-- .settings/org.eclipse.m2e.core.prefs | 4 +++ README.md | 2 +- RedBlackNode.jar | Bin 44381 -> 0 bytes pom.xml | 28 ++++++++++++++++++ .../btrekkie/red_black_node/RedBlackNode.java | 6 ++-- .../btrekkie/red_black_node/Reference.java | 0 .../github/btrekkie/tree_list/TreeList.java | 0 .../btrekkie/tree_list/TreeListNode.java | 0 .../ArbitraryOrderCollection.java | 0 .../ArbitraryOrderNode.java | 0 .../ArbitraryOrderValue.java | 0 .../test/ArbitraryOrderCollectionTest.java | 0 .../btrekkie/interval_tree/IntervalTree.java | 0 .../interval_tree/IntervalTreeInterval.java | 0 .../interval_tree/IntervalTreeNode.java | 0 .../interval_tree/test/IntervalTreeTest.java | 0 .../red_black_node/test/RedBlackNodeTest.java | 0 .../red_black_node/test/TestRedBlackNode.java | 0 .../btrekkie/sub_array_min/SubArrayMin.java | 0 .../sub_array_min/SubArrayMinNode.java | 0 .../sub_array_min/test/SubArrayMinTest.java | 0 .../btrekkie/tree_list/test/TreeListTest.java | 0 target/RedBlackNode-1.0.0.jar | Bin 0 -> 20851 bytes 27 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 .settings/org.eclipse.m2e.core.prefs delete mode 100644 RedBlackNode.jar create mode 100644 pom.xml rename src/{ => main/java}/com/github/btrekkie/red_black_node/RedBlackNode.java (99%) rename src/{ => main/java}/com/github/btrekkie/red_black_node/Reference.java (100%) rename src/{ => main/java}/com/github/btrekkie/tree_list/TreeList.java (100%) rename src/{ => main/java}/com/github/btrekkie/tree_list/TreeListNode.java (100%) rename src/{ => test/java}/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java (100%) rename src/{ => test/java}/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java (100%) rename src/{ => test/java}/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java (100%) rename src/{ => test/java}/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java (100%) rename src/{ => test/java}/com/github/btrekkie/interval_tree/IntervalTree.java (100%) rename src/{ => test/java}/com/github/btrekkie/interval_tree/IntervalTreeInterval.java (100%) rename src/{ => test/java}/com/github/btrekkie/interval_tree/IntervalTreeNode.java (100%) rename src/{ => test/java}/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java (100%) rename src/{ => test/java}/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java (100%) rename src/{ => test/java}/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java (100%) rename src/{ => test/java}/com/github/btrekkie/sub_array_min/SubArrayMin.java (100%) rename src/{ => test/java}/com/github/btrekkie/sub_array_min/SubArrayMinNode.java (100%) rename src/{ => test/java}/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java (100%) rename src/{ => test/java}/com/github/btrekkie/tree_list/test/TreeListTest.java (100%) create mode 100644 target/RedBlackNode-1.0.0.jar diff --git a/.classpath b/.classpath index 72c2ba61a..ad5b70f00 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,28 @@ - - + + + + + + + + + + + - + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 610859901..67fae9804 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* -bin/.gitignore +.DS_Store +target +!target/RedBlackNode*.jar diff --git a/.project b/.project index ec61c803b..423c0a752 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - DataStructures + RedBlackNode @@ -10,8 +10,14 @@ + + org.eclipse.m2e.core.maven2Builder + + + + org.eclipse.m2e.core.maven2Nature org.eclipse.jdt.core.javanature diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 7341ab168..1937c422f 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,11 +1,13 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/README.md b/README.md index ecb3225dc..39bf14c85 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ data and augmentation information to each node. * 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. +* Compatible with Java 6.0 and above. # Limitations * The values of the tree must be stored in the non-leaf nodes. `RedBlackNode` diff --git a/RedBlackNode.jar b/RedBlackNode.jar deleted file mode 100644 index 3a126d57a5ebfdade57e1623ee197a549ef15103..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 44381 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 AE&u=k diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..c6a125803 --- /dev/null +++ b/pom.xml @@ -0,0 +1,28 @@ + + 4.0.0 + com.github.btrekkie.red_black_node + RedBlackNode + 1.0.0 + RedBlackNode + Java implementation of augmented red-black trees. Easily maintain custom augmentation information by subclassing RedBlackNode: the base class does the work for you. + + + + maven-compiler-plugin + 3.8.0 + + 1.6 + 1.6 + + + + + + + junit + junit + 4.12 + test + + + \ No newline at end of file diff --git a/src/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java similarity index 99% rename from src/com/github/btrekkie/red_black_node/RedBlackNode.java rename to src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java index e045b380d..b604d5267 100644 --- a/src/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -355,10 +355,10 @@ public abstract class RedBlackNode> implements Compara } /** Returns a Comparator that compares instances of N using their natural order, as in N.compareTo. */ + @SuppressWarnings({"rawtypes", "unchecked"}) private Comparator naturalOrder() { - @SuppressWarnings("unchecked") - Comparator comparator = (Comparator)NATURAL_ORDER; - return comparator; + Comparator comparator = (Comparator)NATURAL_ORDER; + return (Comparator)comparator; } /** diff --git a/src/com/github/btrekkie/red_black_node/Reference.java b/src/main/java/com/github/btrekkie/red_black_node/Reference.java similarity index 100% rename from src/com/github/btrekkie/red_black_node/Reference.java rename to src/main/java/com/github/btrekkie/red_black_node/Reference.java diff --git a/src/com/github/btrekkie/tree_list/TreeList.java b/src/main/java/com/github/btrekkie/tree_list/TreeList.java similarity index 100% rename from src/com/github/btrekkie/tree_list/TreeList.java rename to src/main/java/com/github/btrekkie/tree_list/TreeList.java diff --git a/src/com/github/btrekkie/tree_list/TreeListNode.java b/src/main/java/com/github/btrekkie/tree_list/TreeListNode.java similarity index 100% rename from src/com/github/btrekkie/tree_list/TreeListNode.java rename to src/main/java/com/github/btrekkie/tree_list/TreeListNode.java diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java b/src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java similarity index 100% rename from src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java rename to src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderCollection.java diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java b/src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java similarity index 100% rename from src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java rename to src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderNode.java diff --git a/src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java b/src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java similarity index 100% rename from src/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java rename to src/test/java/com/github/btrekkie/arbitrary_order_collection/ArbitraryOrderValue.java diff --git a/src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java b/src/test/java/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java similarity index 100% rename from src/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java rename to src/test/java/com/github/btrekkie/arbitrary_order_collection/test/ArbitraryOrderCollectionTest.java diff --git a/src/com/github/btrekkie/interval_tree/IntervalTree.java b/src/test/java/com/github/btrekkie/interval_tree/IntervalTree.java similarity index 100% rename from src/com/github/btrekkie/interval_tree/IntervalTree.java rename to src/test/java/com/github/btrekkie/interval_tree/IntervalTree.java diff --git a/src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java b/src/test/java/com/github/btrekkie/interval_tree/IntervalTreeInterval.java similarity index 100% rename from src/com/github/btrekkie/interval_tree/IntervalTreeInterval.java rename to src/test/java/com/github/btrekkie/interval_tree/IntervalTreeInterval.java diff --git a/src/com/github/btrekkie/interval_tree/IntervalTreeNode.java b/src/test/java/com/github/btrekkie/interval_tree/IntervalTreeNode.java similarity index 100% rename from src/com/github/btrekkie/interval_tree/IntervalTreeNode.java rename to src/test/java/com/github/btrekkie/interval_tree/IntervalTreeNode.java diff --git a/src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java b/src/test/java/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java similarity index 100% rename from src/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java rename to src/test/java/com/github/btrekkie/interval_tree/test/IntervalTreeTest.java diff --git a/src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java b/src/test/java/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java similarity index 100% rename from src/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java rename to src/test/java/com/github/btrekkie/red_black_node/test/RedBlackNodeTest.java diff --git a/src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java b/src/test/java/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java similarity index 100% rename from src/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java rename to src/test/java/com/github/btrekkie/red_black_node/test/TestRedBlackNode.java diff --git a/src/com/github/btrekkie/sub_array_min/SubArrayMin.java b/src/test/java/com/github/btrekkie/sub_array_min/SubArrayMin.java similarity index 100% rename from src/com/github/btrekkie/sub_array_min/SubArrayMin.java rename to src/test/java/com/github/btrekkie/sub_array_min/SubArrayMin.java diff --git a/src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java b/src/test/java/com/github/btrekkie/sub_array_min/SubArrayMinNode.java similarity index 100% rename from src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java rename to src/test/java/com/github/btrekkie/sub_array_min/SubArrayMinNode.java diff --git a/src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java b/src/test/java/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java similarity index 100% rename from src/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java rename to src/test/java/com/github/btrekkie/sub_array_min/test/SubArrayMinTest.java diff --git a/src/com/github/btrekkie/tree_list/test/TreeListTest.java b/src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java similarity index 100% rename from src/com/github/btrekkie/tree_list/test/TreeListTest.java rename to src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java diff --git a/target/RedBlackNode-1.0.0.jar b/target/RedBlackNode-1.0.0.jar new file mode 100644 index 0000000000000000000000000000000000000000..0b2b3dbf9abb7047d7f8564c2ece619173236ffc GIT binary patch literal 20851 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 Tv$BDV7Y4#IAnQ5M0tN;E1h=tA literal 0 HcmV?d00001 From 454a2b8ae7f97a97789e9cd4e8eef7989348cf3a Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Sat, 16 Mar 2019 14:17:40 -0400 Subject: [PATCH 20/24] Minor readability improvements This makes various minor improvements to readability and implementation. --- .gitignore | 2 +- README.md | 1 + pom.xml | 2 +- .../btrekkie/red_black_node/RedBlackNode.java | 54 +++++++++++------- .../github/btrekkie/tree_list/TreeList.java | 7 ++- target/RedBlackNode-1.0.0.jar | Bin 20851 -> 0 bytes target/RedBlackNode-1.0.1.jar | Bin 0 -> 20859 bytes 7 files changed, 40 insertions(+), 26 deletions(-) delete mode 100644 target/RedBlackNode-1.0.0.jar create mode 100644 target/RedBlackNode-1.0.1.jar diff --git a/.gitignore b/.gitignore index 67fae9804..0c2ab5325 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,5 @@ hs_err_pid* .DS_Store -target +target/** !target/RedBlackNode*.jar diff --git a/README.md b/README.md index 39bf14c85..4a5080399 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ data and augmentation information to each node. # 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. + (Note that many data structures can be implemented with either approach.) * 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 diff --git a/pom.xml b/pom.xml index c6a125803..9dd63689b 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 com.github.btrekkie.red_black_node RedBlackNode - 1.0.0 + 1.0.1 RedBlackNode Java implementation of augmented red-black trees. Easily maintain custom augmentation information by subclassing RedBlackNode: the base class does the work for you. diff --git a/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java b/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java index b604d5267..7777b7361 100644 --- a/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java +++ b/src/main/java/com/github/btrekkie/red_black_node/RedBlackNode.java @@ -269,6 +269,7 @@ public abstract class RedBlackNode> implements Compara grandparent.left.isRed = false; grandparent.right.isRed = false; grandparent.isRed = true; + if (changed) { changed = parent.augment(); if (changed) { @@ -288,6 +289,7 @@ public abstract class RedBlackNode> implements Compara node = parent; parent = node.parent; } + if (parent.left == node) { boolean grandparentChanged = grandparent.rotateRight(); if (augment) { @@ -299,6 +301,7 @@ public abstract class RedBlackNode> implements Compara changed = grandparentChanged; } } + parent.isRed = false; grandparent.isRed = true; node = parent; @@ -366,8 +369,8 @@ public abstract class RedBlackNode> implements Compara * solitary node that does not belong to any tree, and we ignore its initial "parent", "left", "right", and isRed * fields. * - * If it is not efficient or convenient for a subclass to find the location for a node using a Comparator, then it - * should manually add the node to the appropriate location, color it red, and call fixInsertion(). + * If it is not efficient or convenient to find the location for a node using a Comparator, then you 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 @@ -544,6 +547,7 @@ public abstract class RedBlackNode> implements Compara } } + // Update augmentation info N parent = sibling.parent; if (changed && parent != null) { if (!haveAugmentedParent) { @@ -594,6 +598,7 @@ public abstract class RedBlackNode> implements Compara } if (child != null) { + // Replace this node with its child child.parent = parent; if (parent != null) { if (parent.left == this) { @@ -603,6 +608,7 @@ public abstract class RedBlackNode> implements Compara } } child.isRed = false; + if (child.parent != null) { N parent; for (parent = child.parent; parent != null; parent = parent.parent) { @@ -612,6 +618,7 @@ public abstract class RedBlackNode> implements Compara } } } else if (parent != null) { + // Replace this node with a leaf node N leaf = left; N parent = this.parent; N sibling; @@ -622,6 +629,7 @@ public abstract class RedBlackNode> implements Compara parent.right = leaf; sibling = parent.left; } + if (!isRed) { RedBlackNode siblingNode = sibling; siblingNode.fixSiblingDeletion(); @@ -704,6 +712,7 @@ public abstract class RedBlackNode> implements Compara N left = createTree(iterator, (size - 1) / 2, height - 1, leaf); N node = iterator.next(); N right = createTree(iterator, size / 2, height - 1, leaf); + node.isRed = height % 4 == 1; node.left = left; node.right = right; @@ -713,6 +722,7 @@ public abstract class RedBlackNode> implements Compara if (!right.isLeaf()) { right.parent = node; } + node.augment(); return node; } @@ -732,10 +742,12 @@ public abstract class RedBlackNode> implements Compara if (size == 0) { return leaf; } + int height = 0; for (int subtreeSize = size; subtreeSize > 0; subtreeSize /= 2) { height++; } + N node = createTree(nodes.iterator(), size, height, leaf); node.parent = null; node.isRed = false; @@ -866,10 +878,10 @@ public abstract class RedBlackNode> implements Compara /** * Splits the tree rooted at this node into two trees, so that the first element of the return value is the root of * a tree consisting of the nodes that were before the specified node, and the second element of the return value is - * the root of a tree consisting of the nodes that were equal to or after the specified node. This method assumes - * that this node is the root. It assumes that this is in the same tree as splitNode. It takes O(log N) time. It - * is considerably more efficient than removing all of the elements after splitNode and then creating a new tree - * from those nodes. + * the root of a tree consisting of the nodes that were equal to or after the specified node. This method is + * destructive, meaning it does not preserve the original tree. It assumes that this node is the root and is in the + * same tree as splitNode. It takes O(log N) time. It is considerably more efficient than removing all of the + * nodes at or after splitNode and then creating a new tree from those nodes. * @param The node at which to split the tree. * @return An array consisting of the resulting trees. */ @@ -895,6 +907,9 @@ public abstract class RedBlackNode> implements Compara if (parent != null) { throw new IllegalArgumentException("This is not the root of a tree"); } + if (isLeaf() || splitNode.isLeaf()) { + throw new IllegalArgumentException("The root or the split node is a leaf"); + } // Create an array containing the path from the root to splitNode int depth = 1; @@ -905,8 +920,7 @@ public abstract class RedBlackNode> implements Compara if (parent != this) { throw new IllegalArgumentException("The split node does not belong to this tree"); } - @SuppressWarnings("unchecked") - N[] path = (N[])Array.newInstance(getClass(), depth); + RedBlackNode[] path = new RedBlackNode[depth]; for (parent = splitNode; parent != null; parent = parent.parent) { depth--; path[depth] = parent; @@ -1052,22 +1066,18 @@ public abstract class RedBlackNode> implements Compara } // Add lastPivot to the post-split tree - if (last == null) { - last = leaf; - } else { - lastPivot.isRed = true; - lastPivot.parent = lastParent; - if (lastParent != null) { - lastParent.left = lastPivot; - } - lastPivot.left = leaf; - lastPivot.right = leaf; - lastPivot.fixInsertionWithoutGettingRoot(false); - for (last = lastPivot; last.parent != null; last = last.parent) { - last.augment(); - } + lastPivot.isRed = true; + lastPivot.parent = lastParent; + if (lastParent != null) { + lastParent.left = lastPivot; + } + lastPivot.left = leaf; + lastPivot.right = leaf; + lastPivot.fixInsertionWithoutGettingRoot(false); + for (last = lastPivot; last.parent != null; last = last.parent) { last.augment(); } + last.augment(); @SuppressWarnings("unchecked") N[] result = (N[])Array.newInstance(getClass(), 2); diff --git a/src/main/java/com/github/btrekkie/tree_list/TreeList.java b/src/main/java/com/github/btrekkie/tree_list/TreeList.java index 41e411d91..3201bbd23 100644 --- a/src/main/java/com/github/btrekkie/tree_list/TreeList.java +++ b/src/main/java/com/github/btrekkie/tree_list/TreeList.java @@ -140,9 +140,8 @@ public class TreeList extends AbstractList { public T remove(int index) { TreeListNode node = getNode(index); modCount++; - T value = node.value; root = node.remove(); - return value; + return node.value; } @Override @@ -151,6 +150,7 @@ public class TreeList extends AbstractList { throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); } modCount++; + if (values.isEmpty()) { return false; } else { @@ -325,6 +325,7 @@ public class TreeList extends AbstractList { } else if (modCount != TreeList.this.modCount) { throw new ConcurrentModificationException(); } + haveCalledNextOrPrevious = true; justCalledNext = true; haveModified = false; @@ -357,6 +358,7 @@ public class TreeList extends AbstractList { } else if (modCount != TreeList.this.modCount) { throw new ConcurrentModificationException(); } + haveCalledNextOrPrevious = true; justCalledNext = false; haveModified = false; @@ -380,6 +382,7 @@ public class TreeList extends AbstractList { } else if (modCount != TreeList.this.modCount) { throw new ConcurrentModificationException(); } + if (justCalledNext) { prevNode.value = value; } else { diff --git a/target/RedBlackNode-1.0.0.jar b/target/RedBlackNode-1.0.0.jar deleted file mode 100644 index 0b2b3dbf9abb7047d7f8564c2ece619173236ffc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20851 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 Tv$BDV7Y4#IAnQ5M0tN;E1h=tA diff --git a/target/RedBlackNode-1.0.1.jar b/target/RedBlackNode-1.0.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..e73187bc816f0f9a67056acbaf745c6e4bad9181 GIT binary patch literal 20859 zcmb4q1CS=cvhM8eSUa|D+qUN)+qSu5+qP}nwr$&bARfS+1iMub-GpK9Phrf0FAWBz4XMmlq5CHEQ};I#ni-fy#n0=NqyNQ%@PDwdH!{@Gx70JR(6P2L{NEhl{0|QPHf^M1Y3AVg z?~Q{4S^a%A<-asyz<&Y(dHzkC^?$JaZ;dM$84CO(6q&z~5Yf{ZSn4@ABr0y(An?O| zp^%B5Dw$HhwCih7&n!Vb!p{kXk`S$sBrOaZU$68e?N_m1rM>Vz4x&c#pk;sNN7Rq& zAx{?&r)J`E-cL3=^hamwbpKcpo#YG4-F-2z!0XpD)Nn=%nH7Vig!@!nKb$&QxPR|+ zCw;3*U`O5YgteS4W^fgH6zk=)V zy%bdGFQhHRfq^5Y;LbMKJ) z_Kn>E)4+*z+RVHvI$1Vy8MkQ5{B#g%G{#QB(ypR|eu=63qokE|@O-V~hVDUZ31_Rr zR=$FmrI~(8cR4DP$KY%wja^h4o7I~_a2jK?y0bEuezv-@)d<-Ck9z+{k*bCV6by%D zbEe4Bg>~41djC|&>;tZUFvmdp8d}ie#Xf~uO^UW;cO5-XG`K1?%spy}i6Etufo?sz zC*e5;W*Wwth~jo!FJtNTt~w95S(jIabx#eDeGH}m+UmfQnDo-`pbxl?8~TD}CZ<7) zb=0ZIW+_&M$uhI5aOSCqMy3)MEp#6~n%dc>oLr^QR>|Ob)WOWy#CM8_SRx2w8Jh~P zF9B*AJKMrJ0~SH249 zj4nLw5)xWVYjR6d3+e?91=Z%9+}f_1+(tU(6|p$*9k>VuOiA-N#kjoE`89Qnw4-P= zE=4eBO%BA_sT6p@{9hZvzxcxvZnukINhQkQMjo#NN<_g+ln z>>WJT;cuH*(wxe+eyA~c+(;4UpTRN2oc0sd=_Nn)JuNl$wcZLKr0?0RYK(*n(x%TBte2m+FCs&IL4?{s{^FocZ+|hEP~8z$B0k z4W6LFOSIy)BYPt3>{@ z+?Z@-{cU8oAqzvJfXPk0&wWwK3Lj9G>PLMmOjOfiC%-xw`KTC=yf|cI;#65VJJqSN zTspM)e4?ISGe)JbZ3g2odOwYeY7>D;{M3|&k=KaV#KyDqGV1E3FK3;+MyOCp+)Uh6 z<7Vfn^8T>f4DB`C)HsMrlgA%uH5ph`Zm_c^vG_LqI~N3ANgbgNox(?T4fSNF5Q~3i zcgoU#SWfeY(gOm(nM_hHMk8X8R)i2P3v0O5Oz$;BI(5F#A!5;VxTFm;mk~v`N zY0-}^u>-Xyl7II!p~qT_yCvmYEOWXP@-3+^TX#t{4(|cSy01)YeR^bqkSW-L(`+Uu zU+G>2xvRb*15^~AEE8K^=xTNeOQaP_9%!RN86rD>N4tUY%vrZi1M z{bSR<<(ANU@gl+BL9u=Dy1nJeHMunSoM{TQF zR~RJZ6=yh#D^CFG4S~3&cgLme8Vf`G1;1r0 zBjNY=0UfQ3=OVFRY`nA=YM;r-(=J%hY-0mIFjr$^Vk2|%sS!n_IW-`Ms?|uEqg{Eu znv_oCVo(;k@V!qQh<6V)@!IM->6&=vb^_0NM}^i|!Fa=H5Z^J5hi~BQ{F%%~JbzW#Ga1PkhfvD_{>))&>N~GL>UQt>%Qh$gCVmsoT=f2&;EyGew-| z?%LaihyydB^OdBQC5}N$XYgM!l@jduN#f?c?(p4Jzeqqx*0fPb*2VAW>6hAkTp?;S zo$}@kTreuho|OIYfHI9ZPA2BLA=&EE$QYe61sBAEUf+LfKc`IyE-0Fm=_RKTNLS&$aUO_p9<->g zM=s5hH@wg^v`AX`jIWV>%6zxgJ1IbtH$jmf#bKP(F%RldqPf8hrt6M2rtzi`|H z+M?$12b+c(2bS*F^b#puSxN(@?Rgd{UX4Y~Id;N%Lgp|Y>r3y8PdHqfvWQr7A~~8A z&FHP9I4@7l+@G?HTNpWu-|*eP`e>|#t<&eg2V-$P!Ki3c3c~KRUfQO1%WV$yptWGb zD<$bvE6}Kv8#A|yZ&rdIkl50_r||`jXV0cdCqJ+!y?Jik_#vyku(kb}zTa}GIp+jFfj$0Fr65fWU&aVDH`Or4g^ z$-!e#XK&Rh=C)sV}EA`OfiiT`cv3j*hdLW~CaSd#j^DdPAVc)6HygA#y z#e;BX)4sx&Zs;pd5aV{0<91Dm`lrQ%bYh^piO|2o4PTH)Z!F?=S>kqGi2AR;5g9Uk z2qU0h1%>>$@D=q_TKZx)VkPWf^Y4=Tiw#UM-rkdT!h2N9arm{COmZ3;8Te z<-<=Ef=?A-H!)b4KE{M;~RVmFjnLji+WWWKG25 z&|?Lya=pODWSA}Oc`(>2A6IYKEfr?Ku^NQ7KD?!|e8-@~Ee&Nq3JWxD?XMg1@|=SY zK2Kp!$2vhWbj&F~OJGS8LUM6*VN(w#$~^l3>>_4sid;d3U!wbfQrv{`F_Nw#xzPDA z%S#9l&{!2YG{D@u;tNb7)g7vKUaWY_33W4|={gaVtHDNGZiNwS{=LqY=#kI(_^ z=Y;EIW(l*o1;A=-mvt`eK?3kjSJq?H>%0rq@cjjX?_vY?)UJ*33_h2I<( z%|p6xs&x#9`h3AwrM;)R(%!br$NY91yT!~m{^R2h~H@HPjW`5-@&3B<=`hu z7uH4YY2;aTaXmDY5G`yrEKyFUxW|5KgW1R42a7`O^E~Kr%taLh_{8Ei!{-+UE_WP1 z95&Qs7Oo6q>_}G-=s}A%#icm+{?1|4^Z=`>OjJrO3f;)^MVi&lCFf%!;N+zxzyi>1!yBU5<&iHHjcsk=lt_qVGqvj`Q z`RQFK0O|_a0zPpFR}ay~s|H-1nTu*H==`-Ly>~b!OqSzuRb_Z|L8kOscRZa5X^dI* z$lF0w>NV`3+F>kKBsNBscfFlCaSY0DO`1T{Rl&3kU3QV3S+z}`_pqNtJA)A?aaz(^ zK%g=^*9Fiei?(*D-E}}N*X}v;#3D#1*o4;akTipgsqCp<_%b~#?VLB|>aop-_mPr= zF6YEn?9qPI9qKda{N(q;;iU6Hesh;2+dI>)Er+;qq#z&4CKWbQ0z% z$98qhP{4$@W8v0knS|LVaTSg0<&1;giWR!4x)Xv75i5}L{aNX+7ykA%gU9y>=}{83 z@khvlKTo8$95ND76D?O_3NK$BUr8ZTQn44H|ZM|()1DK+5$Ib%wDko|&Lb3l8z z@B-$;w=?wc0()cVOTu?gi>qfVAFmb!bM>7Z^0p%%4&_IOb0yplF7eR>8t$*|bXR{S1obHQ9DV?;!L|nKLupvqCg^k$GRG6^O@i>1{p^nnNH}2M8(-P^=EW zt&g=GZE>%5e^l)AbM;tpP0ZK$VvRs?{YrNu+1gosiFxiB<^s#U*M5mQ-b2MP&N!|G znl?f2@G-jIqRM{O6YhwC*%7vcyB?N!!BZZRR3*lpD02;n+%2pKO0G5Y1m_G&u19>K zV@byC!Eghy*axdqEdAirPV~!3BAU`$d1X_?Of3M{gG&g`^sg@=5)&~&u%0F&FGB2s zG$(A2po{HOz!9($<#ek0UUUrCY&-LuiD=l2ZQ^9u{S!=B9M4s8h=!gExZq<)_TTD# zf-R6IA76vj*}g#V;HvX@KD|l9ZRV!6^RM@4v^|w<&VVXUGA-@KR1If3E5HAujq!9T(nO7RR04W}h|Xi=&Gdz{ z02kjWlj4>_^p{z3Em2dZ692jVfC<`#!s626QSL7Z8xwQN*Myt^axc+unmHE0Nfh-( zpLpze=%u}Wd&UY4Y198LBDH3ez>kp(=cpJrF|A6I^-N)~zx-^dm#4j(BG~OzVa}YR+FN_LA z6yfAJ$8L+HwZ$N7E7}rHb7vK~YeXBr?q&0)EqnD!nYdn7*S*;Xh(fs| z79Vj{Zcs-$NiH^&9hhm@8^&_ZIg74ciLGO&viTBfjYui(De04>)F*fv3g~w)BeVx7 z7xKwld_k%jofEv)ZJATWJR1G!J4`6!5=aTVQxKWL<5jzc&;Z#IzEgHJ0?QBt$sC#K z&;JZgGQ^`|wubh|P>FU^nteE_NklS3o(su8#(NcUeYqdmrOk=y4q^C0Wq3=b&$1DL z`2#ZlTF#c#9mVklDzj%}0RM*+WVvxqT|w?-?h$xBm4><)VYv_rThqm_i!6?4%?x&i zHvfQqbI?g-BWw*acr6e%O_9*brQkA3DyKEJ27F4X=X@}cTvujP`_oY&d6UTwIpM8n zwux#G=IUvqWL{~FeR2|QEi@fX1bK!=csXH^&8F zqotY_AblMrHRcYEj{h=T(3Ooh(%g5HO_=$IpW!6j17nb#8(|sD#D&3MRIClNJvZ55-a7`=!01b z@MsSK+Pp}w?1}B0qxy>BGl9sjZnPB6O(h1^mGDJG6R@zri~UoBg^q|iG5SI!DAf<; zFL8=D^)cGb<KhU7T4eM8 z^42P$GfOov31~$QA%pQqBzMo`sIEGXYwnKr7tg_QB`2Lk@;(RydJAQOhC@dqVxSi? z%vCTnU`reh&q^V4&s9FqF9J6}N!-u-jUlAN6h_bx;%$ohY1fA4Q$N5*1wYs#mY`dT z7GVT%=?vLZmJeGOM!!NiY*B$8^wLMeK?2k5Ea?HY!^=WRTp@!U8V5WVp+!;WLE?tN zuTTu_NYbNhhxOmYvte-#vXKn99>F83iUI1W?1;t@<5hE0Cc=cNF$tpd!NSbcooH36 z0%2UH?}PTW9YFaCdCyKbd=7Opl{fjRpq|YFBMGMMU=VL%u8NhR;xjmYW86>>JC`kl zB;VpK;Knq>lb5ia6GEbL7S59^a|s!IE)@5XlPji}AhFrS<+#;pWdSmn|D_yoc3)1} zwBN5^eKc`)Q;_?Yi9}CC=)gN>Nc()mS7yLT8fTDfL%VtlcPXNSfB1c5-lX0+2fVAU zuC8I=I#=3tKFQ*8y8O$_WSHe*~B(~`*`>3Gk zDf7er03U7*?DLJ35vzi3b>eTHqKP35I*bR_2&v$@1gxAcJD*I zJAT}l1wYuX6z_*rk5!w8m3v~Vrgq$lxzKWOOt>a47dtwa6chsBS$A`I^uU8Y%?Z9m zeu975%DvX=?$I$|No6^f<(bnI8fV{G@_;G5WT-PL#T0>u1=B~)CF$ApH+tkcqc#0$Wtfd&Xm%5_c?c|&5K zY$GYA99>{h`_m^WyW2>?X&GK)qP~)(hx^&49r&CJ4XjT-*g8%&r6r?%2nmg%f=^M^ zD`!V-cE!vF(2TazXCG|Fq|@3EyS*gABDEHY7$Xf$R3%f^i*3QK)MrKsXGS{aAe-9s zbr{TK)7c2L7~E`;tQZX6)9xTx+O@L79jt!8mAwVxLH)f)sgR31;Qm2{le_XrHdr~V zN_)#Tjw)N@y(f+Wk4a|DJqU{+TQ4|JZ`t4)6^V+u8mo`^9_i|fO{r~oKOkut>zbNL z!BX!qNO>V}i}Bv+nyE#;rMn#3yaKal)uhnTuN>~Os7QKSGmwn-NOTSLRqqiMac_DgBzD~}wpY)RPK8VUC_z`XrUhSr8xgWTP$s^}E zE;;zzq43c&9EST!bjz4c;k{cw=!}EN`SPf3Y}L&;!ET75LFDfTj=c- z*U(3Y*8f8UeXrb*qg&AUrja3@7q9-sfFa!`hWkatj&z&q{^gt@yPFW|#iNe(kIvKX zcY@a|YF&&?yzmF(f&AH4<+k$#=RMDek{TL_zE6kPI1>>eb$geIZrxmXW`hNOE`~oM3j9*5tbw1h0I!XOR80!qY$qrLoP>L@g!f$P&#`zx zq~j@Kq(_V}Y zFDu-qCDdO;&?gL<&_AY+dm3?r-EHu&qdVupp}j!8UGmYMrKh9To#Mj0dAL;cdT=D0 zTeu+VY%ja&N_s9@;4*D$Y!Z~$*%G@j8U9)hQ$?0+-Wp9w6qV*r|y%h zF1~7k5r7o+M$Ex(d1u`Eb5E0iTSea$<0JvIbib4ZG*dfg7KwIr0?+$Ec zWMu~V++kzq=>bzcw(N+6r|j}L;o^5}3uF8A(pf%g_OLPqCjJq*@>9a$75%3VTsky8 zAay^IN8@dg6IDh;HV9Dl3oVxpTo{0|`B)E}GcDtFNs|v8D zu&AX4Cdd~RuHI!UPZ(pROJudenA-~vtVYyP)k|9~Ke_eV@>7DUF6SDx+Cu%;e#It< zs2?qd&5NIman~)H3rVw|O?G2W-hncM?eBMrcyg{s)U}LfYZ8cN`Ig$4G9! zj3prB?novQS>v_0ggD$NrxKcDWvT`a?UBFmR1DD3Hy7_0`mQEk}M{2T) zk2^iA$|+`BqhUa7Qj1nIb(8KAxDaS@e{#Ypo2Oyw+6R}dMP+##yZM7q5WY@)I_GO; zoBZ^jAZ=&3V{Jg%?n(u}TVA&Q=y2Rsq$2>5cTFZYvw3<}zmj=D^G;C^ z0`9@TCToA1l~JeDFPgUCtR7kqXuc9(^F9c`ATbjLVx(5XHoG5Hi)B(|LkqVx^aUj` zFq-5XN~!HOsW_TFb6g&&3+5Wu?aVz+;6A08C=@xwN)C4Wjz7oXH|GR@aQ5AKJtX7m z`+R_@^^2kr@(#h@Au^3uvI%+z)gKaBOFHGHESj1I}u67VbWj{shL0~AeQXi9@j9qhq9aCYWWBVwI9aUEKa~}Oq z_DDa~xT#pwYnz;wk??Xo4$&lzxY-$Pgb^tbJ#qsY16353B%Y=CLlYNy_Uoe^QS6WO zRM}UDo%wT#&xz{jdYbw2kxQk6f(t{j&eFSc;Qop*GPXO)&;@F#WHohk zBWI!Z<)}e_*ZWl6N*eC$0+f78m2oHP-uBqa!RgDQHGbKKDP`Cn&F+DhXEZr7L!J6B z(5w5hQIMhiw!8L>SW(BSP5nY&*nW`M7=G|!rG8d%LTwXdukz$2x8N9D^K{uK?f*dc z$!6)o+q$l{RWSJ))7;g5g%jNIF;ht`U7K60p~K&}(7#don`$xnz2v7ObDc zi~^<>tQWxyi}?4ed@YG?kQ1;>O_?GM=243==kO~-goI^|va%Az(%o;}wtJ;nV1ce7 zRSR$k(nx0U%8^3nFv&^h(p4Jf^+M$X1xd6T9v)n=_vGeGS0kc5{;Ckk2O65C?YbYS z^2bp1X<`Wp>`G)5KYP+-+Sz}3pPSaL7%1?Bjys}erQV5a)>V#JaSpZyDEzsLrANuG z>5U?Raqc2{5Y3bI<(?q0h4R^=-;+bXGw>c` z4qq3%Y~?`-8i(UPOd>@K%k1|0jUK1vVCt~!K*o3aC>Gi%weGkMUASf}&?)}n)~5zDbD zHA!szd=Q1y1mxOAsZ@Bs;j?$pB3k4A8~Q75DE-u6EyW>e#MY%<1g2ZM=sfN`Bs-uc z6h1rpLwf8JyT}%Xb+4uCDPePP19_gwdJpCFh;OCplqRbR;al8uB<5_2YZy*es$1R} z#C`{iaY4NkEZyx=8bZk(5crk+;PW*;hfYfA2VB|!UvPp$b{tcrSga8+1=vSqh&&QK z*p4rhV~I&joRf>qouHbESAxx}^+&+7;ZMFxvvE0;jEUa;Ni(;6{T!tKQI>e*Qqvql zF@D5alRpG@@O=6VEALcFMb4K9F3jQk6CV{qFBne(As++jZ||)krLP;=MKFuy^`XF* z+i90l53C>}7|kXdAl^pSgn7l0#1wlG%9huvYJ!h{%d*Ud)$e;cWHwT9P=K@vK?mb~@bMWI}EU2=LE3+vq%dW|zn*nGjNRA@Na)+W-rGOfB41E&i}Zd9VS zHdn^?CU2i`QW2mOOdhUm2}0lQnw(LtbAV{qZ-u5Bg{!&1GC`AKHsO(O7De|%&(}4x z9>kyB^$LlaoQ#Z$_e>wOafc52C2W;Q$~1q_%0v&bNFZ3Q!8G<+j7A_$L7G`<5Ml*iSgdQDXEnpXs;qBH@Yn86w^M33pehyBCfC9fShlQ3{P&neJO@gB4xW}zJ5-b;C z{xAGo2AX{&@WyUKRcOLkQ3HEdYjO+ox3M^~p%w+*wa7Gy(vrk-X7m~^`w*;^O*5ipgir-7 zNi+kwGwS3fM&wR5vpa<5jnQ1*rad<*qt2JP69$z z>6wNI6=6TW!!Q;7CPbpEhzKT{ScCF3wcDRx;ME$~+I1?e0ct^AZ*FlMtvRVk63MKt z+@hUaisbE(MpFPGHbQyg@%yHJZj{y{0J2CPzGiPLi>48q{gz`(l1IBU(NE(9WbMe( zo^HH-h9Kc;&G=i;t%~O{v!HV6=P_^fLJq_vL(O1iR#pMV1~Uh7dRO~z*0xS!>YO-y zdFg2S00!s5*;iq~1?SF_6 zY0@F|PDdVj#{rAyB?d3-_9s9V9~;#X-E9ETk7CSV(`VRA&%I87fyIe3>rDX0`arye zTnLuVa8J9b$|?rvwrH`s5sX(v{6LGKg15T~f4o}W_iymwW5Zo2iT&h&2g;0|Wwaqy zv|Pp1i%^wUH7?Nyx3|6%h(g2}XN9<%znA(LQ{5KE%8p5J7Jl`QHPB2CHFg zi!paVcg`qw|GGY#>@xP}&$UU9HA7Cc_v10FhS3XbqnOyC8-W z_$&Ig^CkaUX1z^Pk1r5fMr1z-JZH7vbD>D%o$==#t?fRkeNFFsg?s1+H5;;xe2p>r z0u6?Qfo<&BM!bOaFQd}9H}$dKK@C+jz?`{uWiKVY^DQ1Z?%mm<&>7eGA)JR_jpDA? z^ZcJE*#D@7_=&J|+~7b!j0iwL{Qv*8@E?Wozt+P-l}{C6={u#S3V{(T?$}-dApU)o*o8C6O&Mk_=Zq3{q*W0k#c$rZYwJXE-Opnh$iW^ ztoW???K9a=$~V-92CwbZ)!$@8hY!Ite%_DsX=BsQ^(DlQrNo0xIT3`Sx+C()8`4V42b2R|RbFVkaQqC<0MD6Nx$kF7&Azl;-FG6%;aTXKhygikEnAXp{aU9>#EGf=1&q?VyglU7-q6e z&hv~95_ip6r&F@AwpUgAe6vkE+?vT0KRf-g$#AfV`4Aog5uJ$c)w|Wxrmf%d-c~ei zZ0Y_$KJBF|HQkmTY~+sTQw&i#=sX)sAJ0CnP;_}!R$z^ZUVm?@ARQ_nsHNIVo)+bA zG%@}0MHs80?q&iuBMFYo{cQYZKUzE0kY;!c63ovfs9Gy~I!d1ixoSXBFaaQA+)Gg~ zL3Xc^eNw{Btt}+d%(g;5GW9t3q5G-`0+Sv;)Mw6DykM6xL0}Bg>pPoc3SKPHOUvlpFfaac^r|{6` z*Cv_MpwK|;S@H!U0WS|@Y-_oVJToI%P$mYKxsy5F9YWKmE)*1ymb4%<2)x68wNi~V zq&k#}Nm#ahU{*pK?!qf_P3B2$O330sNqgWh%gu?g4i<{aEtOib1*t(u3ZhCvWmWR~ zozW`5!;*+v9}TtJ?M^|wf`v>lJTPnrLz`qhLESemiJ?72Nmbo|QG4x_<85(xh=p$Z#5r;Y~D7QS2z+eelv0OUQ{@*Gcm&rmOZ9H-If+d zbEmHtx%vWc!>aGsd3TP*dU|aQL8W)qxR9bY`dE!`Y+Kww&(mo2mDuJkWHrcWbco%ArY?qC6t$ zx#qiu;Pi5Y;q%*B7Dk#DvpCL?SZ6~(&lq#bM(;srw`bfRaorMS{lqHo=Qjachq?I- z&V2uw1Eh>lGdBI`q+hIZh@Ul_uR8h4JJC%-bBj=Rt-HKK)y8uiJCLlp*L~EkEY_&3 zo9nzV$0Fm<9`AR=ajOv1;|-wteRpDgu?t57vMDpN4(r>aXZNMDu)#mA+rOped3^72 zu#?T-{ecetZhWKZdIP%j8che{=hoNHBj2MP(&GCo8|5yNzBUII*c41~ee}5{bA?YU zWX`%_7AL3)Q&#SGc2+0!`Bt1e>~v*uSFC9AT?y2t57=gyhd{G6kLf4$T0QjIR~#Ot zgdZIpL3gh345C^}mOs`hoG3=yZ*I}9xGb?OoKNqEm!rA(yOU6G{HrA~jt8NDa*z{o zdhW1ObUk;G)GWIq6Tee&M(%j2=+Otml&sSC8AgSi;z@rRIA?^mqXe=altwA`#8E%9 z2>Zx}AZ^j2(*M|})pe4rNPoNe8hF;=b3-ZaxL#$j#@vFU9GUK5nS%g}?Dr@Epn&zq zHu$qHcBkc+e++*SQ?W4*uhE~_gF}u@_1iRT8bsKe?ypEDrtnJ7^$yPUPBwbM8NXo?{QSYX#ppT6 z7S8RMMH`LFFo3E%E6T=?Y8OYdOZ}EYi<)egAWHhq%(66YP*Jx>Zt zjE_3_lh}n@f@KfVdk>4(*|((~hyAJ-6+uq5&5KxRu zrCy8~mfomw)KYE2ErDn<)?4D{$Cwp1EPv!!$I9JQIBD!PK0Y3SK zT!mzh^JKW><-d(hV$EaDlK%eh=70?ZMD_nXHu>kYOvS?yX%YL&*d=b$GAJMj4F;kG zT)%D<0i2BbhtSVhE5Q;H7-_OTLl-enmUcTA|6U$y{qV}@C37zkPe7S4logJ?x=y9i zY}dA|hsV>(@`5XV&Ad&%SV=v|ANiE=XmOcz@|n zV`JTx>QdTx23Tx{ND`h%FFVFev$MUjOekCch!WUizx%xHPz0;ieDYMmgQ-Xcx!it` zHy^*j`$m8hk%36KUz|)mZ>~v@Vvry6z3%49;$mIuhdD)R!QEb@ao>cc@=j&>^^rBD z_|Ka>DM6q@9x4D_TKf&qm^?lkDtSvum6ki!GUBIn`x`9Atdyje3xsQ4F8H9nQ*96e z5+#`pE27Bojc#`A+1&e{V`&~Y`r`hk`xw&oQ&Ac^-8|&k(FsjwMH%Jo1d;k@gCtb8m1RuF|7?xIJXcbm@~1h1{1K z99)*UzIDGnqy_B}K9F{ri=K}9@nCn5aLv~&iBnNvadHf}r(Ah{Bm6)N<=jv`nRHf6 z213Fg3wn`aCC1jv!e{M)0ZtfI14@t|#zSJKsfJ-$%Zu%rAuI3Hg)ew4FE{u{ieIHc zD3%2)0rY(WB@+_1$K}5(A_5tkC2K)6yjn!~)nv+SWd$jlW^K7q(uu!40)?$Gss>>} zx_)qP;wR28ZUON`IOFo<<&?s0krOsm(^iT8zLbPJE;^4~QNAVNLoIDo?47JPRP4Rc z;@hD5(G3^uM^HP8q`AU_jbKMKubAM7g9E8Xq;)`&*iwK!?>={L*KQvOcJ5lvZBfdXW)%{&;0Sy8E zZWpwv4@=L4#E9gr88-bkCHBsMms0Z%^w&x_k37sQ`l*rKh;lvZMDVW@(s&*wL!y+> z#=c~=f#w&zx^|FBN4nSv;7K7mMqOaV0!BX%WmbvA;D8=B4`cj9RJaU0$VY*mm>|qv zibs<;l*tME`aqBxFt48jdN9Fu@f{ey4UOBkX%b=tM&Mysq40&t>;L#Dgh~C#Yk7Sb zz?Xk!VD6AY@HH;rdqotyVrBlZKI^k{m@fb~bUrdUQL)gSOqXw^BcM zUOiI!sWU8c---wGMsPLM^}J9>K&Srm518MOmSMpU39%#KTfxj+e)1rNg1}257^e-U z6&B}5+b{tQh!{ACa~=L~A+CP}`s4SsICZ0h5X=aP5wdxRBGM}msAbUoNBiylUW$T> zfvxD@PnhjUw)Uui-B5|;p{DFpIy>D56K^Ym3|8<1{%G`@W#p#M8WN19r!XQl4ns~O zUE3rhopqP%LGMG0x|3Nm%nW_!xK4Mdd8Qb?q8z0+f=qGTJ$x<41|tl)!0kY{}lA$wZ;$*wF@#$?6sMJmaymXgtbJf$% zzC1bH@hZckMyyh&&KcidS~6=KW@gTxWSux7xdn~c&T^n5m9C|B{xvb_Eov4k)F;+h zw=r+8nW09J0S#FQn{=D?a5uj04|#h#!kX=Qf8TAm=7GHY;dod*gpEz1!sld)041PE zb}dhG>uU!H5-=#i5?w^cakqo`Ln#KiaPPv7LNL$?3UCmu2iB!Gf2qi9st=IOwI)~6 z0M(>e#3@4hogIF^$Rb6c%6CnnVS{*BmE>Md1<|BktL{5qTIsyDig0X6Jk7O7IbVAS zX*Rx1-WoDJf^_DNF-1_Ii%{-jKM1{%lQj@${z$b%>587tfe~1#x8{zb;!&fKVX1Pn z<8jRd*^woBV3d$uM5mOdO$<^kh|W8ryhBlO=z!R58}N#Msysl?tDmkjG+hOIk>^I~ zifbQ30Npr$lFar%iT!qt{U$T#a{Hn0x|N?tfQy8S;rT&wFVE=0r7z<^w?a)PcV=1w z_ow0rKVfkuv5emHgPuuRPj(vV=?QR=9-z(Wt2)>N47+x`=M+5W%O?R39)jz*gJU@SHfJFY&}g!l_R^cfko!46&7kv zNyn}Z8GhJda>(8*G|zd?GrL(1coLi(LRsnnwE>&^n)sH!8su^-LeoHP*+3J83Inms z=#AiZ)r^&A7-M(jTPb>7yO!|DG^Ic{VeEY-b#1tFNWDG2e8Ht6nS!jFEPIWH-*XFa z@-YNWGl+}0LjuS~2aJxb&uiX-W148*&!iytAzRH&-l%e|igF^!YnKyF zBU;Nr>?l;1vAq{CW~j~M&B5CPtg!tmO}hgiIGBQ1m;yper3xvSX(qR!|NJ%pX081avE_`~ z@%C1WV&IXjTa!W9XQWCKPnvxG%jZ6k?}*g6g)%<4P9go%Z->4;8G%LbyyvkMdhN#9 zh5e!M9u#qXc~L+xM?q}%RJJr4doNQky&l`d_6b|Oy^BN{+m}d2W8;c?u6A&(PyOdR zRFbCXsrWAJlSKla)k-c%znHQflhk%jpw>?PcMVMqr|DtlL~HxW_U7Dv4)O3Uo2iH9 z1om$H@M5}Olk8Hej`GdC>rm~b27}`Ax|QdH*XY7fJzL?<4WL@m7E z^k*{0OW^t=daejk%}%cceyfGiczHK}cQLL0=#f#gzqdw1<=t%D4HvxfnpHpr5hjkY z>88i{whgAqaaOT;Io9WvmCU3;Sc-DZEYafN5HHm zcWfHb=DncO89mw=MQ&<6rmczE2~LfvbE=ZcQK&=FZW+z3sFQMGF32^WSrtt#maT2j z6S{60yWu!vjWdH#>KXtzr*fp{rjFTXGYzkio0&`x}(mqq>y-N~lEX8jOKlimMrmyr0=*v{@byT5sztGby| z<*Jpv`T3gzB5`jy3vc{=*dyuqad!&)rxbRXo9iF*x__G;^iAWi;-zJ8|F8I^Ip;O! z)eGyk7hj(KCAO0J>8aTnf%l43^Jfd6*|tY=*;!MG4GpKH#eRJZ{&R-yZ&CFu%|(*O z{XNx-gpV7oY~nicsW?vHr%ved^bKoDHvSRXzbP{87IS3bii3U6a%}b=%l}l7HG|W* z^3Jlq)4qqv@vlx_mODGvX21Rmzo$Oao<@o}{b%3#Yq9Jr`-kTR!aLd5Px_=^Gw<H4>5%R!%w zD-`BD7riau+b&bn%JlX}>^|qVO-mLSAI{a=#KksA{Qa`7Cli)v2Ys|(7$W$iz}Hng zNkn_5R@=e_{;mvjxvGlpq@6w+)oIDPIHl5}^zw$Tw!=&B-uu$6RW9oD^xuB%7MlvQ zE3=QyFE9@8n7lA(lZZ}+iMDAP_xiT*O>NOyZU$ZjV$tH$S^pmA5KEn4D4OW9q%8XA z4j%D>ilts3o6M@`ey@@{%f0K)ZKRUwBWA!K@;8P%sPKp#`s$E zjG%(YDK^@^(I2F$cjW>%lb2ezWU>~wYF$>j(X#sF&#b3b#hK0-rp_}$eM5K`UcK&V zbaqYA>Nf_~O`dnAKb~Xxo#E4=hh;}+?>@Cn`in%w&QJ%H_SMOQF5jIy?Yiij z6s~?fyQObJt!M506tlVe!isXcU!Sc!F3nzI`XW;CQijQr-yW}ClUV43W*c`IDR*mT%p}JKu75U5(o3%PfM+L!VXO zJ~!R#{o`l+8ow`icyHNa`aep|Z`n6S#`}3zbqr_sd!MW0PoHpzv3AO;`KM$ZngrBN zSRM4*y0K00u&m3i2(6RXW**t)Ezc;gsCZEA#AeoSon2E6#V*aCSkpd1|Fhu7(+aA| ze{2`U8~)t%{hs2N_tnQ|D96>iXI@>Aojk?PasLsEWgMCFguG(TzJB8*ZSHezO}n+< z)3fuK<1JX_n*41KO%PJPVwvB?f9rD14{2C_!94JWADCa@2j1i+mZj#wJI|PkLDTAJ zih(D?=s`|~(Sw}~qlb3X3U15o$ubZ5=oYwRIT+SW$oG5t>io}Jj0_C=z*$*0$_+07 zdafcjr#Iwu-faVcJ>Rv(_RZchd4UFxTRVg87LQwtSi9~qbw!=-aSeKKc+>xVBAS|$ zE;H?Xo19i|q200d+MWd;3c`BJzOQcn{e_2QZC&nxofY3~>W+}@|k>U{66dSfBH=0L@?{vN)M zd*1F`d-3@4Yztpb1e*X1o*vTE{&9;R(`dY!RiJshv&*Sxu=%uPWUGm{_tI_@PLw|+xy8i=fOGYLUX50r} z0lf+Y0t|0K6L*LVj?jkv04$JV5LnWv4kY2_0@86<2oo^Rp91DkC;(Y}0?3A(SA}pY z`k7Xs6RRKqq~jHkiF7O#KC3_{SU~_tqcE_1K+*^+wSXlFZmU5jS|Lnu#bE+Qxrf_8 z&O+Co@y9GIwxvI1oZ7qa=-mgpeNx75OJJ|fIv>vV7|*g>}neFhw1m8(9Mrx!T^h~hzzornrAz?+o~WV|pCmH}Dsffg_@007`Lt3m(( literal 0 HcmV?d00001 From 515a28dcfd2956a5fa956c3ec7c5c17161f07ce4 Mon Sep 17 00:00:00 2001 From: William Jacobs Date: Sat, 30 May 2020 10:33:13 -0400 Subject: [PATCH 21/24] Added reference to documentation webpage This changes README.md to refer to the webpage containing the documentation for the project. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a5080399..9f1b0bd37 100644 --- a/README.md +++ b/README.md @@ -138,5 +138,4 @@ public class Tree> { ``` # Documentation -For more detailed instructions, check the source code to see the full API and -Javadoc documentation. +See for API documentation. From 5e77ef97e458ae539ae070307b83f7f0365c3850 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Oct 2020 12:15:26 +0000 Subject: [PATCH 22/24] Bump junit from 4.12 to 4.13.1 Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1. - [Release notes](https://github.com/junit-team/junit4/releases) - [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md) - [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1) Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9dd63689b..16256898a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ junit junit - 4.12 + 4.13.1 test From 8812f3027b8a307257de2966e3e0b13538e48b93 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Tue, 21 Mar 2023 00:22:45 -0700 Subject: [PATCH 23/24] trimmed for merge --- .classpath | 28 ------ .gitignore | 15 --- .project | 23 ----- .settings/org.eclipse.jdt.core.prefs | 13 --- .settings/org.eclipse.jdt.ui.prefs | 60 ------------ .settings/org.eclipse.m2e.core.prefs | 4 - LICENSE | 21 ---- README.md | 141 --------------------------- pom.xml | 28 ------ target/RedBlackNode-1.0.1.jar | Bin 20859 -> 0 bytes 10 files changed, 333 deletions(-) delete mode 100644 .classpath delete mode 100644 .gitignore delete mode 100644 .project delete mode 100644 .settings/org.eclipse.jdt.core.prefs delete mode 100644 .settings/org.eclipse.jdt.ui.prefs delete mode 100644 .settings/org.eclipse.m2e.core.prefs delete mode 100644 LICENSE delete mode 100644 README.md delete mode 100644 pom.xml delete mode 100644 target/RedBlackNode-1.0.1.jar diff --git a/.classpath b/.classpath deleted file mode 100644 index ad5b70f00..000000000 --- a/.classpath +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0c2ab5325..000000000 --- a/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.class - -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.war -*.ear - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* - -.DS_Store -target/** -!target/RedBlackNode*.jar diff --git a/.project b/.project deleted file mode 100644 index 423c0a752..000000000 --- a/.project +++ /dev/null @@ -1,23 +0,0 @@ - - - RedBlackNode - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 1937c422f..000000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning -org.eclipse.jdt.core.compiler.release=disabled -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs deleted file mode 100644 index a15aefcfd..000000000 --- a/.settings/org.eclipse.jdt.ui.prefs +++ /dev/null @@ -1,60 +0,0 @@ -eclipse.preferences.version=1 -editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true -sp_cleanup.add_default_serial_version_id=true -sp_cleanup.add_generated_serial_version_id=false -sp_cleanup.add_missing_annotations=false -sp_cleanup.add_missing_deprecated_annotations=true -sp_cleanup.add_missing_methods=false -sp_cleanup.add_missing_nls_tags=false -sp_cleanup.add_missing_override_annotations=true -sp_cleanup.add_missing_override_annotations_interface_methods=true -sp_cleanup.add_serial_version_id=false -sp_cleanup.always_use_blocks=true -sp_cleanup.always_use_parentheses_in_expressions=false -sp_cleanup.always_use_this_for_non_static_field_access=false -sp_cleanup.always_use_this_for_non_static_method_access=false -sp_cleanup.convert_functional_interfaces=false -sp_cleanup.convert_to_enhanced_for_loop=false -sp_cleanup.correct_indentation=false -sp_cleanup.format_source_code=false -sp_cleanup.format_source_code_changes_only=false -sp_cleanup.insert_inferred_type_arguments=false -sp_cleanup.make_local_variable_final=true -sp_cleanup.make_parameters_final=false -sp_cleanup.make_private_fields_final=true -sp_cleanup.make_type_abstract_if_missing_method=false -sp_cleanup.make_variable_declarations_final=false -sp_cleanup.never_use_blocks=false -sp_cleanup.never_use_parentheses_in_expressions=true -sp_cleanup.on_save_use_additional_actions=true -sp_cleanup.organize_imports=false -sp_cleanup.qualify_static_field_accesses_with_declaring_class=false -sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true -sp_cleanup.qualify_static_member_accesses_with_declaring_class=false -sp_cleanup.qualify_static_method_accesses_with_declaring_class=false -sp_cleanup.remove_private_constructors=true -sp_cleanup.remove_redundant_type_arguments=true -sp_cleanup.remove_trailing_whitespaces=true -sp_cleanup.remove_trailing_whitespaces_all=true -sp_cleanup.remove_trailing_whitespaces_ignore_empty=false -sp_cleanup.remove_unnecessary_casts=false -sp_cleanup.remove_unnecessary_nls_tags=false -sp_cleanup.remove_unused_imports=false -sp_cleanup.remove_unused_local_variables=false -sp_cleanup.remove_unused_private_fields=true -sp_cleanup.remove_unused_private_members=false -sp_cleanup.remove_unused_private_methods=true -sp_cleanup.remove_unused_private_types=true -sp_cleanup.sort_members=false -sp_cleanup.sort_members_all=false -sp_cleanup.use_anonymous_class_creation=false -sp_cleanup.use_blocks=false -sp_cleanup.use_blocks_only_for_return_and_throw=false -sp_cleanup.use_lambda=true -sp_cleanup.use_parentheses_in_expressions=false -sp_cleanup.use_this_for_non_static_field_access=false -sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true -sp_cleanup.use_this_for_non_static_method_access=false -sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true -sp_cleanup.use_type_arguments=false diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs deleted file mode 100644 index f897a7f1c..000000000 --- a/.settings/org.eclipse.m2e.core.prefs +++ /dev/null @@ -1,4 +0,0 @@ -activeProfiles= -eclipse.preferences.version=1 -resolveWorkspaceProjects=true -version=1 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index b5b29e44d..000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 btrekkie - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md deleted file mode 100644 index 9f1b0bd37..000000000 --- a/README.md +++ /dev/null @@ -1,141 +0,0 @@ -# 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. -* Compatible with Java 6.0 and above. - -# 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. - (Note that many data structures can be implemented with either approach.) -* 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 -```java -class Node extends RedBlackNode> { - /** 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; - } - } -} -``` - -```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 -See for API documentation. diff --git a/pom.xml b/pom.xml deleted file mode 100644 index 16256898a..000000000 --- a/pom.xml +++ /dev/null @@ -1,28 +0,0 @@ - - 4.0.0 - com.github.btrekkie.red_black_node - RedBlackNode - 1.0.1 - RedBlackNode - Java implementation of augmented red-black trees. Easily maintain custom augmentation information by subclassing RedBlackNode: the base class does the work for you. - - - - maven-compiler-plugin - 3.8.0 - - 1.6 - 1.6 - - - - - - - junit - junit - 4.13.1 - test - - - \ No newline at end of file diff --git a/target/RedBlackNode-1.0.1.jar b/target/RedBlackNode-1.0.1.jar deleted file mode 100644 index e73187bc816f0f9a67056acbaf745c6e4bad9181..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20859 zcmb4q1CS=cvhM8eSUa|D+qUN)+qSu5+qP}nwr$&bARfS+1iMub-GpK9Phrf0FAWBz4XMmlq5CHEQ};I#ni-fy#n0=NqyNQ%@PDwdH!{@Gx70JR(6P2L{NEhl{0|QPHf^M1Y3AVg z?~Q{4S^a%A<-asyz<&Y(dHzkC^?$JaZ;dM$84CO(6q&z~5Yf{ZSn4@ABr0y(An?O| zp^%B5Dw$HhwCih7&n!Vb!p{kXk`S$sBrOaZU$68e?N_m1rM>Vz4x&c#pk;sNN7Rq& zAx{?&r)J`E-cL3=^hamwbpKcpo#YG4-F-2z!0XpD)Nn=%nH7Vig!@!nKb$&QxPR|+ zCw;3*U`O5YgteS4W^fgH6zk=)V zy%bdGFQhHRfq^5Y;LbMKJ) z_Kn>E)4+*z+RVHvI$1Vy8MkQ5{B#g%G{#QB(ypR|eu=63qokE|@O-V~hVDUZ31_Rr zR=$FmrI~(8cR4DP$KY%wja^h4o7I~_a2jK?y0bEuezv-@)d<-Ck9z+{k*bCV6by%D zbEe4Bg>~41djC|&>;tZUFvmdp8d}ie#Xf~uO^UW;cO5-XG`K1?%spy}i6Etufo?sz zC*e5;W*Wwth~jo!FJtNTt~w95S(jIabx#eDeGH}m+UmfQnDo-`pbxl?8~TD}CZ<7) zb=0ZIW+_&M$uhI5aOSCqMy3)MEp#6~n%dc>oLr^QR>|Ob)WOWy#CM8_SRx2w8Jh~P zF9B*AJKMrJ0~SH249 zj4nLw5)xWVYjR6d3+e?91=Z%9+}f_1+(tU(6|p$*9k>VuOiA-N#kjoE`89Qnw4-P= zE=4eBO%BA_sT6p@{9hZvzxcxvZnukINhQkQMjo#NN<_g+ln z>>WJT;cuH*(wxe+eyA~c+(;4UpTRN2oc0sd=_Nn)JuNl$wcZLKr0?0RYK(*n(x%TBte2m+FCs&IL4?{s{^FocZ+|hEP~8z$B0k z4W6LFOSIy)BYPt3>{@ z+?Z@-{cU8oAqzvJfXPk0&wWwK3Lj9G>PLMmOjOfiC%-xw`KTC=yf|cI;#65VJJqSN zTspM)e4?ISGe)JbZ3g2odOwYeY7>D;{M3|&k=KaV#KyDqGV1E3FK3;+MyOCp+)Uh6 z<7Vfn^8T>f4DB`C)HsMrlgA%uH5ph`Zm_c^vG_LqI~N3ANgbgNox(?T4fSNF5Q~3i zcgoU#SWfeY(gOm(nM_hHMk8X8R)i2P3v0O5Oz$;BI(5F#A!5;VxTFm;mk~v`N zY0-}^u>-Xyl7II!p~qT_yCvmYEOWXP@-3+^TX#t{4(|cSy01)YeR^bqkSW-L(`+Uu zU+G>2xvRb*15^~AEE8K^=xTNeOQaP_9%!RN86rD>N4tUY%vrZi1M z{bSR<<(ANU@gl+BL9u=Dy1nJeHMunSoM{TQF zR~RJZ6=yh#D^CFG4S~3&cgLme8Vf`G1;1r0 zBjNY=0UfQ3=OVFRY`nA=YM;r-(=J%hY-0mIFjr$^Vk2|%sS!n_IW-`Ms?|uEqg{Eu znv_oCVo(;k@V!qQh<6V)@!IM->6&=vb^_0NM}^i|!Fa=H5Z^J5hi~BQ{F%%~JbzW#Ga1PkhfvD_{>))&>N~GL>UQt>%Qh$gCVmsoT=f2&;EyGew-| z?%LaihyydB^OdBQC5}N$XYgM!l@jduN#f?c?(p4Jzeqqx*0fPb*2VAW>6hAkTp?;S zo$}@kTreuho|OIYfHI9ZPA2BLA=&EE$QYe61sBAEUf+LfKc`IyE-0Fm=_RKTNLS&$aUO_p9<->g zM=s5hH@wg^v`AX`jIWV>%6zxgJ1IbtH$jmf#bKP(F%RldqPf8hrt6M2rtzi`|H z+M?$12b+c(2bS*F^b#puSxN(@?Rgd{UX4Y~Id;N%Lgp|Y>r3y8PdHqfvWQr7A~~8A z&FHP9I4@7l+@G?HTNpWu-|*eP`e>|#t<&eg2V-$P!Ki3c3c~KRUfQO1%WV$yptWGb zD<$bvE6}Kv8#A|yZ&rdIkl50_r||`jXV0cdCqJ+!y?Jik_#vyku(kb}zTa}GIp+jFfj$0Fr65fWU&aVDH`Or4g^ z$-!e#XK&Rh=C)sV}EA`OfiiT`cv3j*hdLW~CaSd#j^DdPAVc)6HygA#y z#e;BX)4sx&Zs;pd5aV{0<91Dm`lrQ%bYh^piO|2o4PTH)Z!F?=S>kqGi2AR;5g9Uk z2qU0h1%>>$@D=q_TKZx)VkPWf^Y4=Tiw#UM-rkdT!h2N9arm{COmZ3;8Te z<-<=Ef=?A-H!)b4KE{M;~RVmFjnLji+WWWKG25 z&|?Lya=pODWSA}Oc`(>2A6IYKEfr?Ku^NQ7KD?!|e8-@~Ee&Nq3JWxD?XMg1@|=SY zK2Kp!$2vhWbj&F~OJGS8LUM6*VN(w#$~^l3>>_4sid;d3U!wbfQrv{`F_Nw#xzPDA z%S#9l&{!2YG{D@u;tNb7)g7vKUaWY_33W4|={gaVtHDNGZiNwS{=LqY=#kI(_^ z=Y;EIW(l*o1;A=-mvt`eK?3kjSJq?H>%0rq@cjjX?_vY?)UJ*33_h2I<( z%|p6xs&x#9`h3AwrM;)R(%!br$NY91yT!~m{^R2h~H@HPjW`5-@&3B<=`hu z7uH4YY2;aTaXmDY5G`yrEKyFUxW|5KgW1R42a7`O^E~Kr%taLh_{8Ei!{-+UE_WP1 z95&Qs7Oo6q>_}G-=s}A%#icm+{?1|4^Z=`>OjJrO3f;)^MVi&lCFf%!;N+zxzyi>1!yBU5<&iHHjcsk=lt_qVGqvj`Q z`RQFK0O|_a0zPpFR}ay~s|H-1nTu*H==`-Ly>~b!OqSzuRb_Z|L8kOscRZa5X^dI* z$lF0w>NV`3+F>kKBsNBscfFlCaSY0DO`1T{Rl&3kU3QV3S+z}`_pqNtJA)A?aaz(^ zK%g=^*9Fiei?(*D-E}}N*X}v;#3D#1*o4;akTipgsqCp<_%b~#?VLB|>aop-_mPr= zF6YEn?9qPI9qKda{N(q;;iU6Hesh;2+dI>)Er+;qq#z&4CKWbQ0z% z$98qhP{4$@W8v0knS|LVaTSg0<&1;giWR!4x)Xv75i5}L{aNX+7ykA%gU9y>=}{83 z@khvlKTo8$95ND76D?O_3NK$BUr8ZTQn44H|ZM|()1DK+5$Ib%wDko|&Lb3l8z z@B-$;w=?wc0()cVOTu?gi>qfVAFmb!bM>7Z^0p%%4&_IOb0yplF7eR>8t$*|bXR{S1obHQ9DV?;!L|nKLupvqCg^k$GRG6^O@i>1{p^nnNH}2M8(-P^=EW zt&g=GZE>%5e^l)AbM;tpP0ZK$VvRs?{YrNu+1gosiFxiB<^s#U*M5mQ-b2MP&N!|G znl?f2@G-jIqRM{O6YhwC*%7vcyB?N!!BZZRR3*lpD02;n+%2pKO0G5Y1m_G&u19>K zV@byC!Eghy*axdqEdAirPV~!3BAU`$d1X_?Of3M{gG&g`^sg@=5)&~&u%0F&FGB2s zG$(A2po{HOz!9($<#ek0UUUrCY&-LuiD=l2ZQ^9u{S!=B9M4s8h=!gExZq<)_TTD# zf-R6IA76vj*}g#V;HvX@KD|l9ZRV!6^RM@4v^|w<&VVXUGA-@KR1If3E5HAujq!9T(nO7RR04W}h|Xi=&Gdz{ z02kjWlj4>_^p{z3Em2dZ692jVfC<`#!s626QSL7Z8xwQN*Myt^axc+unmHE0Nfh-( zpLpze=%u}Wd&UY4Y198LBDH3ez>kp(=cpJrF|A6I^-N)~zx-^dm#4j(BG~OzVa}YR+FN_LA z6yfAJ$8L+HwZ$N7E7}rHb7vK~YeXBr?q&0)EqnD!nYdn7*S*;Xh(fs| z79Vj{Zcs-$NiH^&9hhm@8^&_ZIg74ciLGO&viTBfjYui(De04>)F*fv3g~w)BeVx7 z7xKwld_k%jofEv)ZJATWJR1G!J4`6!5=aTVQxKWL<5jzc&;Z#IzEgHJ0?QBt$sC#K z&;JZgGQ^`|wubh|P>FU^nteE_NklS3o(su8#(NcUeYqdmrOk=y4q^C0Wq3=b&$1DL z`2#ZlTF#c#9mVklDzj%}0RM*+WVvxqT|w?-?h$xBm4><)VYv_rThqm_i!6?4%?x&i zHvfQqbI?g-BWw*acr6e%O_9*brQkA3DyKEJ27F4X=X@}cTvujP`_oY&d6UTwIpM8n zwux#G=IUvqWL{~FeR2|QEi@fX1bK!=csXH^&8F zqotY_AblMrHRcYEj{h=T(3Ooh(%g5HO_=$IpW!6j17nb#8(|sD#D&3MRIClNJvZ55-a7`=!01b z@MsSK+Pp}w?1}B0qxy>BGl9sjZnPB6O(h1^mGDJG6R@zri~UoBg^q|iG5SI!DAf<; zFL8=D^)cGb<KhU7T4eM8 z^42P$GfOov31~$QA%pQqBzMo`sIEGXYwnKr7tg_QB`2Lk@;(RydJAQOhC@dqVxSi? z%vCTnU`reh&q^V4&s9FqF9J6}N!-u-jUlAN6h_bx;%$ohY1fA4Q$N5*1wYs#mY`dT z7GVT%=?vLZmJeGOM!!NiY*B$8^wLMeK?2k5Ea?HY!^=WRTp@!U8V5WVp+!;WLE?tN zuTTu_NYbNhhxOmYvte-#vXKn99>F83iUI1W?1;t@<5hE0Cc=cNF$tpd!NSbcooH36 z0%2UH?}PTW9YFaCdCyKbd=7Opl{fjRpq|YFBMGMMU=VL%u8NhR;xjmYW86>>JC`kl zB;VpK;Knq>lb5ia6GEbL7S59^a|s!IE)@5XlPji}AhFrS<+#;pWdSmn|D_yoc3)1} zwBN5^eKc`)Q;_?Yi9}CC=)gN>Nc()mS7yLT8fTDfL%VtlcPXNSfB1c5-lX0+2fVAU zuC8I=I#=3tKFQ*8y8O$_WSHe*~B(~`*`>3Gk zDf7er03U7*?DLJ35vzi3b>eTHqKP35I*bR_2&v$@1gxAcJD*I zJAT}l1wYuX6z_*rk5!w8m3v~Vrgq$lxzKWOOt>a47dtwa6chsBS$A`I^uU8Y%?Z9m zeu975%DvX=?$I$|No6^f<(bnI8fV{G@_;G5WT-PL#T0>u1=B~)CF$ApH+tkcqc#0$Wtfd&Xm%5_c?c|&5K zY$GYA99>{h`_m^WyW2>?X&GK)qP~)(hx^&49r&CJ4XjT-*g8%&r6r?%2nmg%f=^M^ zD`!V-cE!vF(2TazXCG|Fq|@3EyS*gABDEHY7$Xf$R3%f^i*3QK)MrKsXGS{aAe-9s zbr{TK)7c2L7~E`;tQZX6)9xTx+O@L79jt!8mAwVxLH)f)sgR31;Qm2{le_XrHdr~V zN_)#Tjw)N@y(f+Wk4a|DJqU{+TQ4|JZ`t4)6^V+u8mo`^9_i|fO{r~oKOkut>zbNL z!BX!qNO>V}i}Bv+nyE#;rMn#3yaKal)uhnTuN>~Os7QKSGmwn-NOTSLRqqiMac_DgBzD~}wpY)RPK8VUC_z`XrUhSr8xgWTP$s^}E zE;;zzq43c&9EST!bjz4c;k{cw=!}EN`SPf3Y}L&;!ET75LFDfTj=c- z*U(3Y*8f8UeXrb*qg&AUrja3@7q9-sfFa!`hWkatj&z&q{^gt@yPFW|#iNe(kIvKX zcY@a|YF&&?yzmF(f&AH4<+k$#=RMDek{TL_zE6kPI1>>eb$geIZrxmXW`hNOE`~oM3j9*5tbw1h0I!XOR80!qY$qrLoP>L@g!f$P&#`zx zq~j@Kq(_V}Y zFDu-qCDdO;&?gL<&_AY+dm3?r-EHu&qdVupp}j!8UGmYMrKh9To#Mj0dAL;cdT=D0 zTeu+VY%ja&N_s9@;4*D$Y!Z~$*%G@j8U9)hQ$?0+-Wp9w6qV*r|y%h zF1~7k5r7o+M$Ex(d1u`Eb5E0iTSea$<0JvIbib4ZG*dfg7KwIr0?+$Ec zWMu~V++kzq=>bzcw(N+6r|j}L;o^5}3uF8A(pf%g_OLPqCjJq*@>9a$75%3VTsky8 zAay^IN8@dg6IDh;HV9Dl3oVxpTo{0|`B)E}GcDtFNs|v8D zu&AX4Cdd~RuHI!UPZ(pROJudenA-~vtVYyP)k|9~Ke_eV@>7DUF6SDx+Cu%;e#It< zs2?qd&5NIman~)H3rVw|O?G2W-hncM?eBMrcyg{s)U}LfYZ8cN`Ig$4G9! zj3prB?novQS>v_0ggD$NrxKcDWvT`a?UBFmR1DD3Hy7_0`mQEk}M{2T) zk2^iA$|+`BqhUa7Qj1nIb(8KAxDaS@e{#Ypo2Oyw+6R}dMP+##yZM7q5WY@)I_GO; zoBZ^jAZ=&3V{Jg%?n(u}TVA&Q=y2Rsq$2>5cTFZYvw3<}zmj=D^G;C^ z0`9@TCToA1l~JeDFPgUCtR7kqXuc9(^F9c`ATbjLVx(5XHoG5Hi)B(|LkqVx^aUj` zFq-5XN~!HOsW_TFb6g&&3+5Wu?aVz+;6A08C=@xwN)C4Wjz7oXH|GR@aQ5AKJtX7m z`+R_@^^2kr@(#h@Au^3uvI%+z)gKaBOFHGHESj1I}u67VbWj{shL0~AeQXi9@j9qhq9aCYWWBVwI9aUEKa~}Oq z_DDa~xT#pwYnz;wk??Xo4$&lzxY-$Pgb^tbJ#qsY16353B%Y=CLlYNy_Uoe^QS6WO zRM}UDo%wT#&xz{jdYbw2kxQk6f(t{j&eFSc;Qop*GPXO)&;@F#WHohk zBWI!Z<)}e_*ZWl6N*eC$0+f78m2oHP-uBqa!RgDQHGbKKDP`Cn&F+DhXEZr7L!J6B z(5w5hQIMhiw!8L>SW(BSP5nY&*nW`M7=G|!rG8d%LTwXdukz$2x8N9D^K{uK?f*dc z$!6)o+q$l{RWSJ))7;g5g%jNIF;ht`U7K60p~K&}(7#don`$xnz2v7ObDc zi~^<>tQWxyi}?4ed@YG?kQ1;>O_?GM=243==kO~-goI^|va%Az(%o;}wtJ;nV1ce7 zRSR$k(nx0U%8^3nFv&^h(p4Jf^+M$X1xd6T9v)n=_vGeGS0kc5{;Ckk2O65C?YbYS z^2bp1X<`Wp>`G)5KYP+-+Sz}3pPSaL7%1?Bjys}erQV5a)>V#JaSpZyDEzsLrANuG z>5U?Raqc2{5Y3bI<(?q0h4R^=-;+bXGw>c` z4qq3%Y~?`-8i(UPOd>@K%k1|0jUK1vVCt~!K*o3aC>Gi%weGkMUASf}&?)}n)~5zDbD zHA!szd=Q1y1mxOAsZ@Bs;j?$pB3k4A8~Q75DE-u6EyW>e#MY%<1g2ZM=sfN`Bs-uc z6h1rpLwf8JyT}%Xb+4uCDPePP19_gwdJpCFh;OCplqRbR;al8uB<5_2YZy*es$1R} z#C`{iaY4NkEZyx=8bZk(5crk+;PW*;hfYfA2VB|!UvPp$b{tcrSga8+1=vSqh&&QK z*p4rhV~I&joRf>qouHbESAxx}^+&+7;ZMFxvvE0;jEUa;Ni(;6{T!tKQI>e*Qqvql zF@D5alRpG@@O=6VEALcFMb4K9F3jQk6CV{qFBne(As++jZ||)krLP;=MKFuy^`XF* z+i90l53C>}7|kXdAl^pSgn7l0#1wlG%9huvYJ!h{%d*Ud)$e;cWHwT9P=K@vK?mb~@bMWI}EU2=LE3+vq%dW|zn*nGjNRA@Na)+W-rGOfB41E&i}Zd9VS zHdn^?CU2i`QW2mOOdhUm2}0lQnw(LtbAV{qZ-u5Bg{!&1GC`AKHsO(O7De|%&(}4x z9>kyB^$LlaoQ#Z$_e>wOafc52C2W;Q$~1q_%0v&bNFZ3Q!8G<+j7A_$L7G`<5Ml*iSgdQDXEnpXs;qBH@Yn86w^M33pehyBCfC9fShlQ3{P&neJO@gB4xW}zJ5-b;C z{xAGo2AX{&@WyUKRcOLkQ3HEdYjO+ox3M^~p%w+*wa7Gy(vrk-X7m~^`w*;^O*5ipgir-7 zNi+kwGwS3fM&wR5vpa<5jnQ1*rad<*qt2JP69$z z>6wNI6=6TW!!Q;7CPbpEhzKT{ScCF3wcDRx;ME$~+I1?e0ct^AZ*FlMtvRVk63MKt z+@hUaisbE(MpFPGHbQyg@%yHJZj{y{0J2CPzGiPLi>48q{gz`(l1IBU(NE(9WbMe( zo^HH-h9Kc;&G=i;t%~O{v!HV6=P_^fLJq_vL(O1iR#pMV1~Uh7dRO~z*0xS!>YO-y zdFg2S00!s5*;iq~1?SF_6 zY0@F|PDdVj#{rAyB?d3-_9s9V9~;#X-E9ETk7CSV(`VRA&%I87fyIe3>rDX0`arye zTnLuVa8J9b$|?rvwrH`s5sX(v{6LGKg15T~f4o}W_iymwW5Zo2iT&h&2g;0|Wwaqy zv|Pp1i%^wUH7?Nyx3|6%h(g2}XN9<%znA(LQ{5KE%8p5J7Jl`QHPB2CHFg zi!paVcg`qw|GGY#>@xP}&$UU9HA7Cc_v10FhS3XbqnOyC8-W z_$&Ig^CkaUX1z^Pk1r5fMr1z-JZH7vbD>D%o$==#t?fRkeNFFsg?s1+H5;;xe2p>r z0u6?Qfo<&BM!bOaFQd}9H}$dKK@C+jz?`{uWiKVY^DQ1Z?%mm<&>7eGA)JR_jpDA? z^ZcJE*#D@7_=&J|+~7b!j0iwL{Qv*8@E?Wozt+P-l}{C6={u#S3V{(T?$}-dApU)o*o8C6O&Mk_=Zq3{q*W0k#c$rZYwJXE-Opnh$iW^ ztoW???K9a=$~V-92CwbZ)!$@8hY!Ite%_DsX=BsQ^(DlQrNo0xIT3`Sx+C()8`4V42b2R|RbFVkaQqC<0MD6Nx$kF7&Azl;-FG6%;aTXKhygikEnAXp{aU9>#EGf=1&q?VyglU7-q6e z&hv~95_ip6r&F@AwpUgAe6vkE+?vT0KRf-g$#AfV`4Aog5uJ$c)w|Wxrmf%d-c~ei zZ0Y_$KJBF|HQkmTY~+sTQw&i#=sX)sAJ0CnP;_}!R$z^ZUVm?@ARQ_nsHNIVo)+bA zG%@}0MHs80?q&iuBMFYo{cQYZKUzE0kY;!c63ovfs9Gy~I!d1ixoSXBFaaQA+)Gg~ zL3Xc^eNw{Btt}+d%(g;5GW9t3q5G-`0+Sv;)Mw6DykM6xL0}Bg>pPoc3SKPHOUvlpFfaac^r|{6` z*Cv_MpwK|;S@H!U0WS|@Y-_oVJToI%P$mYKxsy5F9YWKmE)*1ymb4%<2)x68wNi~V zq&k#}Nm#ahU{*pK?!qf_P3B2$O330sNqgWh%gu?g4i<{aEtOib1*t(u3ZhCvWmWR~ zozW`5!;*+v9}TtJ?M^|wf`v>lJTPnrLz`qhLESemiJ?72Nmbo|QG4x_<85(xh=p$Z#5r;Y~D7QS2z+eelv0OUQ{@*Gcm&rmOZ9H-If+d zbEmHtx%vWc!>aGsd3TP*dU|aQL8W)qxR9bY`dE!`Y+Kww&(mo2mDuJkWHrcWbco%ArY?qC6t$ zx#qiu;Pi5Y;q%*B7Dk#DvpCL?SZ6~(&lq#bM(;srw`bfRaorMS{lqHo=Qjachq?I- z&V2uw1Eh>lGdBI`q+hIZh@Ul_uR8h4JJC%-bBj=Rt-HKK)y8uiJCLlp*L~EkEY_&3 zo9nzV$0Fm<9`AR=ajOv1;|-wteRpDgu?t57vMDpN4(r>aXZNMDu)#mA+rOped3^72 zu#?T-{ecetZhWKZdIP%j8che{=hoNHBj2MP(&GCo8|5yNzBUII*c41~ee}5{bA?YU zWX`%_7AL3)Q&#SGc2+0!`Bt1e>~v*uSFC9AT?y2t57=gyhd{G6kLf4$T0QjIR~#Ot zgdZIpL3gh345C^}mOs`hoG3=yZ*I}9xGb?OoKNqEm!rA(yOU6G{HrA~jt8NDa*z{o zdhW1ObUk;G)GWIq6Tee&M(%j2=+Otml&sSC8AgSi;z@rRIA?^mqXe=altwA`#8E%9 z2>Zx}AZ^j2(*M|})pe4rNPoNe8hF;=b3-ZaxL#$j#@vFU9GUK5nS%g}?Dr@Epn&zq zHu$qHcBkc+e++*SQ?W4*uhE~_gF}u@_1iRT8bsKe?ypEDrtnJ7^$yPUPBwbM8NXo?{QSYX#ppT6 z7S8RMMH`LFFo3E%E6T=?Y8OYdOZ}EYi<)egAWHhq%(66YP*Jx>Zt zjE_3_lh}n@f@KfVdk>4(*|((~hyAJ-6+uq5&5KxRu zrCy8~mfomw)KYE2ErDn<)?4D{$Cwp1EPv!!$I9JQIBD!PK0Y3SK zT!mzh^JKW><-d(hV$EaDlK%eh=70?ZMD_nXHu>kYOvS?yX%YL&*d=b$GAJMj4F;kG zT)%D<0i2BbhtSVhE5Q;H7-_OTLl-enmUcTA|6U$y{qV}@C37zkPe7S4logJ?x=y9i zY}dA|hsV>(@`5XV&Ad&%SV=v|ANiE=XmOcz@|n zV`JTx>QdTx23Tx{ND`h%FFVFev$MUjOekCch!WUizx%xHPz0;ieDYMmgQ-Xcx!it` zHy^*j`$m8hk%36KUz|)mZ>~v@Vvry6z3%49;$mIuhdD)R!QEb@ao>cc@=j&>^^rBD z_|Ka>DM6q@9x4D_TKf&qm^?lkDtSvum6ki!GUBIn`x`9Atdyje3xsQ4F8H9nQ*96e z5+#`pE27Bojc#`A+1&e{V`&~Y`r`hk`xw&oQ&Ac^-8|&k(FsjwMH%Jo1d;k@gCtb8m1RuF|7?xIJXcbm@~1h1{1K z99)*UzIDGnqy_B}K9F{ri=K}9@nCn5aLv~&iBnNvadHf}r(Ah{Bm6)N<=jv`nRHf6 z213Fg3wn`aCC1jv!e{M)0ZtfI14@t|#zSJKsfJ-$%Zu%rAuI3Hg)ew4FE{u{ieIHc zD3%2)0rY(WB@+_1$K}5(A_5tkC2K)6yjn!~)nv+SWd$jlW^K7q(uu!40)?$Gss>>} zx_)qP;wR28ZUON`IOFo<<&?s0krOsm(^iT8zLbPJE;^4~QNAVNLoIDo?47JPRP4Rc z;@hD5(G3^uM^HP8q`AU_jbKMKubAM7g9E8Xq;)`&*iwK!?>={L*KQvOcJ5lvZBfdXW)%{&;0Sy8E zZWpwv4@=L4#E9gr88-bkCHBsMms0Z%^w&x_k37sQ`l*rKh;lvZMDVW@(s&*wL!y+> z#=c~=f#w&zx^|FBN4nSv;7K7mMqOaV0!BX%WmbvA;D8=B4`cj9RJaU0$VY*mm>|qv zibs<;l*tME`aqBxFt48jdN9Fu@f{ey4UOBkX%b=tM&Mysq40&t>;L#Dgh~C#Yk7Sb zz?Xk!VD6AY@HH;rdqotyVrBlZKI^k{m@fb~bUrdUQL)gSOqXw^BcM zUOiI!sWU8c---wGMsPLM^}J9>K&Srm518MOmSMpU39%#KTfxj+e)1rNg1}257^e-U z6&B}5+b{tQh!{ACa~=L~A+CP}`s4SsICZ0h5X=aP5wdxRBGM}msAbUoNBiylUW$T> zfvxD@PnhjUw)Uui-B5|;p{DFpIy>D56K^Ym3|8<1{%G`@W#p#M8WN19r!XQl4ns~O zUE3rhopqP%LGMG0x|3Nm%nW_!xK4Mdd8Qb?q8z0+f=qGTJ$x<41|tl)!0kY{}lA$wZ;$*wF@#$?6sMJmaymXgtbJf$% zzC1bH@hZckMyyh&&KcidS~6=KW@gTxWSux7xdn~c&T^n5m9C|B{xvb_Eov4k)F;+h zw=r+8nW09J0S#FQn{=D?a5uj04|#h#!kX=Qf8TAm=7GHY;dod*gpEz1!sld)041PE zb}dhG>uU!H5-=#i5?w^cakqo`Ln#KiaPPv7LNL$?3UCmu2iB!Gf2qi9st=IOwI)~6 z0M(>e#3@4hogIF^$Rb6c%6CnnVS{*BmE>Md1<|BktL{5qTIsyDig0X6Jk7O7IbVAS zX*Rx1-WoDJf^_DNF-1_Ii%{-jKM1{%lQj@${z$b%>587tfe~1#x8{zb;!&fKVX1Pn z<8jRd*^woBV3d$uM5mOdO$<^kh|W8ryhBlO=z!R58}N#Msysl?tDmkjG+hOIk>^I~ zifbQ30Npr$lFar%iT!qt{U$T#a{Hn0x|N?tfQy8S;rT&wFVE=0r7z<^w?a)PcV=1w z_ow0rKVfkuv5emHgPuuRPj(vV=?QR=9-z(Wt2)>N47+x`=M+5W%O?R39)jz*gJU@SHfJFY&}g!l_R^cfko!46&7kv zNyn}Z8GhJda>(8*G|zd?GrL(1coLi(LRsnnwE>&^n)sH!8su^-LeoHP*+3J83Inms z=#AiZ)r^&A7-M(jTPb>7yO!|DG^Ic{VeEY-b#1tFNWDG2e8Ht6nS!jFEPIWH-*XFa z@-YNWGl+}0LjuS~2aJxb&uiX-W148*&!iytAzRH&-l%e|igF^!YnKyF zBU;Nr>?l;1vAq{CW~j~M&B5CPtg!tmO}hgiIGBQ1m;yper3xvSX(qR!|NJ%pX081avE_`~ z@%C1WV&IXjTa!W9XQWCKPnvxG%jZ6k?}*g6g)%<4P9go%Z->4;8G%LbyyvkMdhN#9 zh5e!M9u#qXc~L+xM?q}%RJJr4doNQky&l`d_6b|Oy^BN{+m}d2W8;c?u6A&(PyOdR zRFbCXsrWAJlSKla)k-c%znHQflhk%jpw>?PcMVMqr|DtlL~HxW_U7Dv4)O3Uo2iH9 z1om$H@M5}Olk8Hej`GdC>rm~b27}`Ax|QdH*XY7fJzL?<4WL@m7E z^k*{0OW^t=daejk%}%cceyfGiczHK}cQLL0=#f#gzqdw1<=t%D4HvxfnpHpr5hjkY z>88i{whgAqaaOT;Io9WvmCU3;Sc-DZEYafN5HHm zcWfHb=DncO89mw=MQ&<6rmczE2~LfvbE=ZcQK&=FZW+z3sFQMGF32^WSrtt#maT2j z6S{60yWu!vjWdH#>KXtzr*fp{rjFTXGYzkio0&`x}(mqq>y-N~lEX8jOKlimMrmyr0=*v{@byT5sztGby| z<*Jpv`T3gzB5`jy3vc{=*dyuqad!&)rxbRXo9iF*x__G;^iAWi;-zJ8|F8I^Ip;O! z)eGyk7hj(KCAO0J>8aTnf%l43^Jfd6*|tY=*;!MG4GpKH#eRJZ{&R-yZ&CFu%|(*O z{XNx-gpV7oY~nicsW?vHr%ved^bKoDHvSRXzbP{87IS3bii3U6a%}b=%l}l7HG|W* z^3Jlq)4qqv@vlx_mODGvX21Rmzo$Oao<@o}{b%3#Yq9Jr`-kTR!aLd5Px_=^Gw<H4>5%R!%w zD-`BD7riau+b&bn%JlX}>^|qVO-mLSAI{a=#KksA{Qa`7Cli)v2Ys|(7$W$iz}Hng zNkn_5R@=e_{;mvjxvGlpq@6w+)oIDPIHl5}^zw$Tw!=&B-uu$6RW9oD^xuB%7MlvQ zE3=QyFE9@8n7lA(lZZ}+iMDAP_xiT*O>NOyZU$ZjV$tH$S^pmA5KEn4D4OW9q%8XA z4j%D>ilts3o6M@`ey@@{%f0K)ZKRUwBWA!K@;8P%sPKp#`s$E zjG%(YDK^@^(I2F$cjW>%lb2ezWU>~wYF$>j(X#sF&#b3b#hK0-rp_}$eM5K`UcK&V zbaqYA>Nf_~O`dnAKb~Xxo#E4=hh;}+?>@Cn`in%w&QJ%H_SMOQF5jIy?Yiij z6s~?fyQObJt!M506tlVe!isXcU!Sc!F3nzI`XW;CQijQr-yW}ClUV43W*c`IDR*mT%p}JKu75U5(o3%PfM+L!VXO zJ~!R#{o`l+8ow`icyHNa`aep|Z`n6S#`}3zbqr_sd!MW0PoHpzv3AO;`KM$ZngrBN zSRM4*y0K00u&m3i2(6RXW**t)Ezc;gsCZEA#AeoSon2E6#V*aCSkpd1|Fhu7(+aA| ze{2`U8~)t%{hs2N_tnQ|D96>iXI@>Aojk?PasLsEWgMCFguG(TzJB8*ZSHezO}n+< z)3fuK<1JX_n*41KO%PJPVwvB?f9rD14{2C_!94JWADCa@2j1i+mZj#wJI|PkLDTAJ zih(D?=s`|~(Sw}~qlb3X3U15o$ubZ5=oYwRIT+SW$oG5t>io}Jj0_C=z*$*0$_+07 zdafcjr#Iwu-faVcJ>Rv(_RZchd4UFxTRVg87LQwtSi9~qbw!=-aSeKKc+>xVBAS|$ zE;H?Xo19i|q200d+MWd;3c`BJzOQcn{e_2QZC&nxofY3~>W+}@|k>U{66dSfBH=0L@?{vN)M zd*1F`d-3@4Yztpb1e*X1o*vTE{&9;R(`dY!RiJshv&*Sxu=%uPWUGm{_tI_@PLw|+xy8i=fOGYLUX50r} z0lf+Y0t|0K6L*LVj?jkv04$JV5LnWv4kY2_0@86<2oo^Rp91DkC;(Y}0?3A(SA}pY z`k7Xs6RRKqq~jHkiF7O#KC3_{SU~_tqcE_1K+*^+wSXlFZmU5jS|Lnu#bE+Qxrf_8 z&O+Co@y9GIwxvI1oZ7qa=-mgpeNx75OJJ|fIv>vV7|*g>}neFhw1m8(9Mrx!T^h~hzzornrAz?+o~WV|pCmH}Dsffg_@007`Lt3m(( From e15cf933aa52e622fb13a88025a2e343bf6db484 Mon Sep 17 00:00:00 2001 From: Leijurv Date: Tue, 21 Mar 2023 00:25:23 -0700 Subject: [PATCH 24/24] don't need tree list --- .../github/btrekkie/tree_list/TreeList.java | 461 --------------- .../btrekkie/tree_list/TreeListNode.java | 40 -- .../btrekkie/tree_list/test/TreeListTest.java | 556 ------------------ 3 files changed, 1057 deletions(-) delete mode 100644 src/main/java/com/github/btrekkie/tree_list/TreeList.java delete mode 100644 src/main/java/com/github/btrekkie/tree_list/TreeListNode.java delete mode 100644 src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java diff --git a/src/main/java/com/github/btrekkie/tree_list/TreeList.java b/src/main/java/com/github/btrekkie/tree_list/TreeList.java deleted file mode 100644 index 3201bbd23..000000000 --- a/src/main/java/com/github/btrekkie/tree_list/TreeList.java +++ /dev/null @@ -1,461 +0,0 @@ -package com.github.btrekkie.tree_list; -import java.util.AbstractList; -import java.util.ArrayList; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -import com.github.btrekkie.red_black_node.RedBlackNode; - -/** - * Implements a list using a self-balancing binary search tree augmented by subtree size. The benefit of this compared - * to ArrayList or LinkedList is that it supports both decent random access and quickly adding to or removing from the - * middle of the list. Operations have the following running times: - * - * size(): O(1) - * get, set, add, remove: O(log N) - * addAll: O(log N + P), where P is the number of elements we add - * iterator(): O(log N + P + M log N), where P is the number of elements over which we iterate and M is the number of - * elements we remove - * listIterator: O(log N + P + M log N + R log N), where P is the number of times we iterate over or set an element, M - * is the number of elements we add or remove, and R is the number of times we change the direction of iteration - * clear(): O(1), excluding garbage collection - * subList.clear(): O(log N), excluding garbage collection - * Constructor: O(N) - * - * This class is similar to an Apache Commons Collections class by the same name. I speculate that the Apache class is - * faster than this (by a constant factor) for most operations. However, this class's implementations of addAll and - * subList.clear() are asymptotically faster than the Apache class's implementations. - */ -public class TreeList extends AbstractList { - /** The dummy leaf node. */ - private final TreeListNode leaf = new TreeListNode(null); - - /** The root node of the tree. */ - private TreeListNode root; - - /** Constructs a new empty TreeList. */ - public TreeList() { - root = leaf; - } - - /** Constructs a new TreeList containing the specified values, in iteration order. */ - public TreeList(Collection values) { - root = createTree(values); - } - - /** Returns the root of a perfectly height-balanced tree containing the specified values, in iteration order. */ - private TreeListNode createTree(Collection values) { - List> nodes = new ArrayList>(values.size()); - for (T value : values) { - nodes.add(new TreeListNode(value)); - } - return RedBlackNode.>createTree(nodes, leaf); - } - - /** - * Returns the node for get(index). Throws an IndexOutOfBoundsException if "index" is not in the range [0, size()). - */ - private TreeListNode getNode(int index) { - if (index < 0 || index >= root.size) { - throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + ")"); - } - int rank = index; - TreeListNode node = root; - while (rank != node.left.size) { - if (rank < node.left.size) { - node = node.left; - } else { - rank -= node.left.size + 1; - node = node.right; - } - } - return node; - } - - @Override - public T get(int index) { - return getNode(index).value; - } - - @Override - public int size() { - return root.size; - } - - @Override - public boolean isEmpty() { - return root == leaf; - } - - @Override - public T set(int index, T value) { - modCount++; - TreeListNode node = getNode(index); - T oldValue = node.value; - node.value = value; - return oldValue; - } - - @Override - public void add(int index, T value) { - if (index < 0 || index > root.size) { - throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); - } - modCount++; - - TreeListNode newNode = new TreeListNode(value); - newNode.left = leaf; - newNode.right = leaf; - if (root.isLeaf()) { - root = newNode; - newNode.isRed = false; - return; - } - newNode.isRed = true; - if (index < root.size) { - TreeListNode node = getNode(index); - if (node.left.isLeaf()) { - node.left = newNode; - newNode.parent = node; - } else { - node = node.predecessor(); - node.right = newNode; - newNode.parent = node; - } - } else { - TreeListNode node; - node = root.max(); - node.right = newNode; - newNode.parent = node; - } - - root = newNode.fixInsertion(); - } - - @Override - public T remove(int index) { - TreeListNode node = getNode(index); - modCount++; - root = node.remove(); - return node.value; - } - - @Override - public boolean addAll(int index, Collection values) { - if (index < 0 || index > root.size) { - throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); - } - modCount++; - - if (values.isEmpty()) { - return false; - } else { - if (index >= root.size) { - root = root.concatenate(createTree(values)); - } else { - TreeListNode[] split = root.split(getNode(index)); - root = split[0].concatenate(createTree(values)).concatenate(split[1]); - } - return true; - } - } - - @Override - public boolean addAll(Collection values) { - modCount++; - if (values.isEmpty()) { - return false; - } else { - root = root.concatenate(createTree(values)); - return true; - } - } - - @Override - protected void removeRange(int startIndex, int endIndex) { - if (startIndex != endIndex) { - modCount++; - TreeListNode last; - if (endIndex == root.size) { - last = leaf; - } else { - TreeListNode[] split = root.split(getNode(endIndex)); - root = split[0]; - last = split[1]; - } - TreeListNode first = root.split(getNode(startIndex))[0]; - root = first.concatenate(last); - } - } - - @Override - public void clear() { - modCount++; - root = leaf; - } - - /** The class for TreeList.iterator(). */ - private class TreeListIterator implements Iterator { - /** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */ - private int modCount = TreeList.this.modCount; - - /** - * The node containing the last element next() returned. This is null if we have yet to call next() or we have - * called remove() since the last call to next(). - */ - private TreeListNode node; - - /** The node containing next(). This is null if we have reached the end of the list. */ - private TreeListNode nextNode; - - /** Whether we have (successfully) called next(). */ - private boolean haveCalledNext; - - private TreeListIterator() { - if (root.isLeaf()) { - nextNode = null; - } else { - nextNode = root.min(); - } - } - - @Override - public boolean hasNext() { - return nextNode != null; - } - - @Override - public T next() { - if (nextNode == null) { - throw new NoSuchElementException("Reached the end of the list"); - } else if (TreeList.this.modCount != modCount) { - throw new ConcurrentModificationException(); - } - haveCalledNext = true; - node = nextNode; - nextNode = nextNode.successor(); - return node.value; - } - - @Override - public void remove() { - if (node == null) { - if (!haveCalledNext) { - throw new IllegalStateException("Must call next() before calling remove()"); - } else { - throw new IllegalStateException("Already removed this element"); - } - } else if (TreeList.this.modCount != modCount) { - throw new ConcurrentModificationException(); - } - root = node.remove(); - node = null; - TreeList.this.modCount++; - modCount = TreeList.this.modCount; - } - } - - @Override - public Iterator iterator() { - return new TreeListIterator(); - } - - /** The class for TreeList.listIterator. */ - private class TreeListListIterator implements ListIterator { - /** The value of TreeList.this.modCount we require to continue iteration without concurrent modification. */ - private int modCount = TreeList.this.modCount; - - /** The current return value for nextIndex(). */ - private int nextIndex; - - /** The node for next(), or null if hasNext() is false. */ - private TreeListNode nextNode; - - /** The node for previous(), or null if hasPrevious() is false. */ - private TreeListNode prevNode; - - /** Whether we have called next() or previous(). */ - private boolean haveCalledNextOrPrevious; - - /** Whether we (successfully) called next() more recently than previous(). */ - private boolean justCalledNext; - - /** - * Whether we have (successfully) called remove() or "add" since the last (successful) call to next() or - * previous(). - */ - private boolean haveModified; - - /** - * Constructs a new TreeListListIterator. - * @param index The starting index, as in the "index" argument to listIterator. This method assumes that - * 0 <= index < root.size. - */ - private TreeListListIterator(int index) { - nextIndex = index; - if (index > 0) { - prevNode = getNode(index - 1); - nextNode = prevNode.successor(); - } else { - prevNode = null; - if (root.size > 0) { - nextNode = root.min(); - } else { - nextNode = null; - } - } - } - - @Override - public boolean hasNext() { - if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - return nextNode != null; - } - - @Override - public T next() { - if (nextNode == null) { - throw new NoSuchElementException("Reached the end of the list"); - } else if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - - haveCalledNextOrPrevious = true; - justCalledNext = true; - haveModified = false; - nextIndex++; - prevNode = nextNode; - nextNode = nextNode.successor(); - return prevNode.value; - } - - @Override - public int nextIndex() { - if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - return nextIndex; - } - - @Override - public boolean hasPrevious() { - if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - return prevNode != null; - } - - @Override - public T previous() { - if (prevNode == null) { - throw new NoSuchElementException("Reached the beginning of the list"); - } else if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - - haveCalledNextOrPrevious = true; - justCalledNext = false; - haveModified = false; - nextIndex--; - nextNode = prevNode; - prevNode = prevNode.predecessor(); - return nextNode.value; - } - - @Override - public int previousIndex() { - return nextIndex - 1; - } - - @Override - public void set(T value) { - if (!haveCalledNextOrPrevious) { - throw new IllegalStateException("Must call next() or previous() before calling \"set\""); - } else if (haveModified) { - throw new IllegalStateException("Already modified the list at this position"); - } else if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - - if (justCalledNext) { - prevNode.value = value; - } else { - nextNode.value = value; - } - TreeList.this.modCount++; - modCount = TreeList.this.modCount; - } - - @Override - public void add(T value) { - if (haveModified) { - throw new IllegalStateException("Already modified the list at this position"); - } else if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - - // Create the new node - TreeListNode newNode = new TreeListNode(value); - newNode.left = leaf; - newNode.right = leaf; - newNode.isRed = true; - - // Insert newNode. There is guaranteed to be a leaf child of prevNode or nextNode where we can insert it. - if (nextNode != null && nextNode.left.isLeaf()) { - nextNode.left = newNode; - newNode.parent = nextNode; - } else if (prevNode != null) { - prevNode.right = newNode; - newNode.parent = prevNode; - } else { - root = newNode; - } - - prevNode = newNode; - root = newNode.fixInsertion(); - nextIndex++; - haveModified = true; - TreeList.this.modCount++; - modCount = TreeList.this.modCount; - } - - @Override - public void remove() { - if (!haveCalledNextOrPrevious) { - throw new IllegalStateException("Must call next() or previous() before calling remove()"); - } else if (haveModified) { - throw new IllegalStateException("Already modified the list at this position"); - } else if (modCount != TreeList.this.modCount) { - throw new ConcurrentModificationException(); - } - - if (justCalledNext) { - TreeListNode predecessor = prevNode.predecessor(); - root = prevNode.remove(); - prevNode = predecessor; - } else { - TreeListNode successor = nextNode.successor(); - root = nextNode.remove(); - nextNode = successor; - } - - haveModified = true; - TreeList.this.modCount++; - modCount = TreeList.this.modCount; - } - } - - @Override - public ListIterator listIterator(int index) { - if (index < 0 || index > root.size) { - throw new IndexOutOfBoundsException("Index " + index + " is not in the range [0, " + root.size + "]"); - } - return new TreeListListIterator(index); - } -} diff --git a/src/main/java/com/github/btrekkie/tree_list/TreeListNode.java b/src/main/java/com/github/btrekkie/tree_list/TreeListNode.java deleted file mode 100644 index de78368db..000000000 --- a/src/main/java/com/github/btrekkie/tree_list/TreeListNode.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.btrekkie.tree_list; - -import com.github.btrekkie.red_black_node.RedBlackNode; - -/** A node in a TreeList. See the comments for TreeList. */ -class TreeListNode extends RedBlackNode> { - /** The element stored in the node. The value is unspecified if this is a leaf node. */ - public T value; - - /** The number of elements in the subtree rooted at this node. */ - public int size; - - public TreeListNode(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; - } - } - - @Override - public void assertNodeIsValid() { - int expectedSize; - if (isLeaf()) { - expectedSize = 0; - } else { - expectedSize = left.size + right.size + 1; - } - if (size != expectedSize) { - throw new RuntimeException("The node's size does not match that of the children"); - } - } -} diff --git a/src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java b/src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java deleted file mode 100644 index e01a63810..000000000 --- a/src/test/java/com/github/btrekkie/tree_list/test/TreeListTest.java +++ /dev/null @@ -1,556 +0,0 @@ -package com.github.btrekkie.tree_list.test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; -import java.util.NoSuchElementException; - -import org.junit.Test; - -import com.github.btrekkie.tree_list.TreeList; - -public class TreeListTest { - /** Tests TreeList.add. */ - @Test - public void testAdd() { - List list = new TreeList(); - assertEquals(Collections.emptyList(), list); - assertEquals(0, list.size()); - assertTrue(list.isEmpty()); - - list.add(4); - list.add(17); - list.add(-4); - list.add(null); - assertEquals(Arrays.asList(4, 17, -4, null), list); - assertEquals(4, list.size()); - assertFalse(list.isEmpty()); - assertEquals(-4, list.get(2).intValue()); - - boolean threwException; - try { - list.get(-3); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.get(9); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.add(-3, 5); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.add(9, 10); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - - list.add(1, 6); - list.add(0, -1); - list.add(6, 42); - assertEquals(Arrays.asList(-1, 4, 6, 17, -4, null, 42), list); - assertEquals(7, list.size()); - assertEquals(6, list.get(2).intValue()); - assertEquals(null, list.get(5)); - - list = new TreeList(); - for (int i = 0; i < 200; i++) { - list.add(i + 300); - } - for (int i = 0; i < 300; i++) { - list.add(i, i); - } - for (int i = 499; i >= 0; i--) { - list.add(500, i + 500); - } - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(777, list.get(777).intValue()); - assertEquals(123, list.get(123).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - } - - /** Tests TreeList.remove. */ - @Test - public void testRemove() { - List list = new TreeList(); - list.add(17); - list.add(-5); - list.add(null); - list.add(0); - list.add(1, 16); - assertEquals(null, list.remove(3)); - assertEquals(17, list.remove(0).intValue()); - assertEquals(0, list.remove(2).intValue()); - assertEquals(Arrays.asList(16, -5), list); - assertEquals(2, list.size()); - assertFalse(list.isEmpty()); - assertEquals(16, list.get(0).intValue()); - assertEquals(-5, list.get(1).intValue()); - - boolean threwException; - try { - list.remove(-3); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.remove(9); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - - list = new TreeList(); - for (int i = 0; i < 1000; i++) { - list.add(i); - } - for (int i = 0; i < 250; i++) { - assertEquals(2 * i + 501, list.remove(501).intValue()); - assertEquals(2 * i + 1, list.remove(i + 1).intValue()); - } - List expected = new ArrayList(500); - for (int i = 0; i < 500; i++) { - expected.add(2 * i); - } - assertEquals(expected, list); - assertEquals(500, list.size()); - assertFalse(list.isEmpty()); - assertEquals(0, list.get(0).intValue()); - assertEquals(998, list.get(499).intValue()); - assertEquals(84, list.get(42).intValue()); - assertEquals(602, list.get(301).intValue()); - } - - /** Tests TreeList.set.*/ - @Test - public void testSet() { - List list = new TreeList(); - list.addAll(Arrays.asList(5, 17, 42, -6, null, 3, null)); - list.set(3, 12); - list.set(0, 6); - list.set(6, 88); - boolean threwException; - try { - list.set(7, 2); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.set(-1, 4); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - assertEquals(Arrays.asList(6, 17, 42, 12, null, 3, 88), list); - assertEquals(7, list.size()); - assertFalse(list.isEmpty()); - assertEquals(42, list.get(2).intValue()); - assertEquals(6, list.get(0).intValue()); - assertEquals(88, list.get(6).intValue()); - - list = new TreeList(); - for (int i = 0; i < 1000; i++) { - list.add(999 - i); - } - for (int i = 0; i < 500; i++) { - list.set(i, i); - list.set(i + 500, i + 500); - } - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(123, list.get(123).intValue()); - assertEquals(777, list.get(777).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - } - - /** Tests TreeList.addAll. */ - @Test - public void testAddAll() { - List list = new TreeList(); - list.add(3); - list.add(42); - list.add(16); - list.addAll(0, Arrays.asList(15, 4, -1)); - list.addAll(2, Arrays.asList(6, 14)); - list.addAll(1, Collections.emptyList()); - list.addAll(8, Arrays.asList(null, 7)); - list.addAll(Arrays.asList(-5, 5)); - assertEquals(Arrays.asList(15, 4, 6, 14, -1, 3, 42, 16, null, 7, -5, 5), list); - assertEquals(12, list.size()); - assertFalse(list.isEmpty()); - assertEquals(14, list.get(3).intValue()); - assertEquals(15, list.get(0).intValue()); - assertEquals(5, list.get(11).intValue()); - - list = new TreeList(); - list.add(7); - list.addAll(Collections.singleton(37)); - assertEquals(Arrays.asList(7, 37), list); - - list = new TreeList(); - List list2 = new ArrayList(400); - for (int i = 0; i < 200; i++) { - list2.add(i + 100); - } - for (int i = 0; i < 200; i++) { - list2.add(i + 700); - } - list.addAll(list2); - list2.clear(); - for (int i = 0; i < 100; i++) { - list2.add(i); - } - list.addAll(0, list2); - list2.clear(); - for (int i = 0; i < 400; i++) { - list2.add(i + 300); - } - list.addAll(300, list2); - list2.clear(); - for (int i = 0; i < 100; i++) { - list2.add(i + 900); - } - list.addAll(900, list2); - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(123, list.get(123).intValue()); - assertEquals(777, list.get(777).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - } - - /** Tests TreeList.subList.clear(). */ - @Test - public void testClearSubList() { - List list = new TreeList(); - list.addAll(Arrays.asList(6, 42, -3, 15, 7, 99, 6, 12)); - list.subList(2, 4).clear(); - list.subList(0, 1).clear(); - list.subList(4, 4).clear(); - list.subList(3, 5).clear(); - assertEquals(Arrays.asList(42, 7, 99), list); - assertEquals(3, list.size()); - assertFalse(list.isEmpty()); - assertEquals(42, list.get(0).intValue()); - assertEquals(7, list.get(1).intValue()); - assertEquals(99, list.get(2).intValue()); - - list = new TreeList(); - for (int i = 0; i < 200; i++) { - list.add(-1); - } - for (int i = 0; i < 500; i++) { - list.add(i); - } - for (int i = 0; i < 400; i++) { - list.add(-1); - } - for (int i = 0; i < 500; i++) { - list.add(i + 500); - } - for (int i = 0; i < 600; i++) { - list.add(-1); - } - list.subList(1600, 2200).clear(); - list.subList(777, 777).clear(); - list.subList(700, 1100).clear(); - list.subList(0, 200).clear(); - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(123, list.get(123).intValue()); - assertEquals(777, list.get(777).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - - list = new TreeList(); - list.addAll(Arrays.asList(-3, null, 8, 14, 9, 42)); - list.subList(0, 6).clear(); - assertEquals(Collections.emptyList(), list); - assertEquals(0, list.size()); - assertTrue(list.isEmpty()); - } - - /** Tests TreeList(Collection). */ - @Test - public void testConstructor() { - List list = new TreeList(Arrays.asList(1, 5, 42, -6, null, 3)); - assertEquals(Arrays.asList(1, 5, 42, -6, null, 3), list); - assertEquals(6, list.size()); - assertFalse(list.isEmpty()); - assertEquals(42, list.get(2).intValue()); - assertEquals(1, list.get(0).intValue()); - assertEquals(3, list.get(5).intValue()); - - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - list = new TreeList(expected); - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(123, list.get(123).intValue()); - assertEquals(777, list.get(777).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - } - - /** Tests TreeList.clear(). */ - @Test - public void testClear() { - List list = new TreeList(); - list.addAll(Arrays.asList(7, 16, 5, 42)); - list.clear(); - assertEquals(Collections.emptyList(), list); - assertEquals(0, list.size()); - assertTrue(list.isEmpty()); - - for (int i = 0; i < 1000; i++) { - list.add(i); - } - list.clear(); - assertEquals(Collections.emptyList(), list); - assertEquals(0, list.size()); - assertTrue(list.isEmpty()); - } - - /** Tests TreeList.iterator(). */ - @Test - public void testIterator() { - List list = new TreeList(); - Iterator iterator = list.iterator(); - assertFalse(iterator.hasNext()); - boolean threwException; - try { - iterator.next(); - threwException = false; - } catch (NoSuchElementException exception) { - threwException = true; - } - assertTrue(threwException); - - list = new TreeList(Arrays.asList(42, 16, null, 7, 8, 3, 12)); - iterator = list.iterator(); - try { - iterator.remove(); - threwException = false; - } catch (IllegalStateException exception) { - threwException = true; - } - assertTrue(threwException); - assertTrue(iterator.hasNext()); - assertEquals(42, iterator.next().intValue()); - assertEquals(16, iterator.next().intValue()); - assertEquals(null, iterator.next()); - assertTrue(iterator.hasNext()); - assertEquals(7, iterator.next().intValue()); - iterator.remove(); - try { - iterator.remove(); - threwException = false; - } catch (IllegalStateException exception) { - threwException = true; - } - assertTrue(threwException); - assertTrue(iterator.hasNext()); - assertTrue(iterator.hasNext()); - assertEquals(8, iterator.next().intValue()); - assertTrue(iterator.hasNext()); - assertEquals(3, iterator.next().intValue()); - assertTrue(iterator.hasNext()); - assertEquals(12, iterator.next().intValue()); - assertFalse(iterator.hasNext()); - iterator.remove(); - assertFalse(iterator.hasNext()); - try { - iterator.next(); - threwException = false; - } catch (NoSuchElementException exception) { - threwException = true; - } - assertTrue(threwException); - assertEquals(Arrays.asList(42, 16, null, 8, 3), list); - - list = new TreeList(); - for (int i = 0; i < 1000; i++) { - list.add(i); - } - iterator = list.iterator(); - for (int i = 0; i < 500; i++) { - assertTrue(iterator.hasNext()); - assertEquals(2 * i, iterator.next().intValue()); - assertTrue(iterator.hasNext()); - assertEquals(2 * i + 1, iterator.next().intValue()); - iterator.remove(); - } - assertFalse(iterator.hasNext()); - List expected = new ArrayList(500); - for (int i = 0; i < 500; i++) { - expected.add(2 * i); - } - assertEquals(expected, list); - } - - /** Tests TreeList.listIterator. */ - @Test - public void testListIterator() { - List list = new TreeList(); - list.addAll(Arrays.asList(7, 16, 42, -3, 12, 25, 8, 9)); - boolean threwException; - try { - list.listIterator(-1); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - try { - list.listIterator(18); - threwException = false; - } catch (IndexOutOfBoundsException exception) { - threwException = true; - } - assertTrue(threwException); - ListIterator iterator = list.listIterator(1); - assertTrue(iterator.hasNext()); - assertTrue(iterator.hasPrevious()); - assertEquals(16, iterator.next().intValue()); - iterator.add(6); - try { - iterator.set(-1); - threwException = false; - } catch (IllegalStateException exception) { - threwException = true; - } - assertTrue(threwException); - try { - iterator.add(9); - threwException = false; - } catch (IllegalStateException exception) { - threwException = true; - } - assertTrue(threwException); - try { - iterator.remove(); - threwException = false; - } catch (IllegalStateException exception) { - threwException = true; - } - assertTrue(threwException); - assertEquals(6, iterator.previous().intValue()); - assertEquals(6, iterator.next().intValue()); - assertEquals(2, iterator.previousIndex()); - assertEquals(3, iterator.nextIndex()); - assertEquals(42, iterator.next().intValue()); - assertEquals(-3, iterator.next().intValue()); - assertEquals(12, iterator.next().intValue()); - iterator.remove(); - assertEquals(-3, iterator.previous().intValue()); - iterator.remove(); - assertEquals(25, iterator.next().intValue()); - iterator.set(14); - assertEquals(8, iterator.next().intValue()); - assertEquals(9, iterator.next().intValue()); - assertFalse(iterator.hasNext()); - try { - iterator.next(); - threwException = false; - } catch (NoSuchElementException exception) { - threwException = true; - } - assertTrue(threwException); - assertEquals(9, iterator.previous().intValue()); - iterator.set(10); - assertEquals(Arrays.asList(7, 16, 6, 42, 14, 8, 10), list); - assertEquals(7, list.size()); - assertFalse(list.isEmpty()); - assertEquals(42, list.get(3).intValue()); - assertEquals(7, list.get(0).intValue()); - assertEquals(10, list.get(6).intValue()); - - list = new TreeList(); - for (int i = 0; i < 1000; i++) { - list.add(-1); - } - iterator = list.listIterator(); - for (int i = 0; i < 500; i++) { - iterator.add(i); - iterator.next(); - } - for (int i = 0; i < 500; i++) { - iterator.previous(); - iterator.remove(); - iterator.previous(); - } - iterator = list.listIterator(500); - for (int i = 0; i < 250; i++) { - iterator.next(); - iterator.set(2 * i + 500); - iterator.next(); - } - for (int i = 0; i < 250; i++) { - iterator.previous(); - iterator.set(999 - 2 * i); - iterator.previous(); - } - List expected = new ArrayList(1000); - for (int i = 0; i < 1000; i++) { - expected.add(i); - } - assertEquals(expected, list); - assertEquals(1000, list.size()); - assertFalse(list.isEmpty()); - assertEquals(123, list.get(123).intValue()); - assertEquals(777, list.get(777).intValue()); - assertEquals(0, list.get(0).intValue()); - assertEquals(999, list.get(999).intValue()); - } -}