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 0b2b3dbf9..000000000
Binary files a/target/RedBlackNode-1.0.0.jar and /dev/null differ
diff --git a/target/RedBlackNode-1.0.1.jar b/target/RedBlackNode-1.0.1.jar
new file mode 100644
index 000000000..e73187bc8
Binary files /dev/null and b/target/RedBlackNode-1.0.1.jar differ