Added test for "lca"
This adds SubArrayMinTest, which tests RedBlackNode.lca.
This commit is contained in:
@@ -9,14 +9,16 @@ 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.
|
||||
* methods for performing various standard operations.
|
||||
*
|
||||
* 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 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.
|
||||
*
|
||||
@@ -539,6 +541,9 @@ public abstract class RedBlackNode<N extends RedBlackNode<N>> 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<N extends RedBlackNode<N>> 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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
85
src/com/github/btrekkie/sub_array_min/SubArrayMin.java
Normal file
85
src/com/github/btrekkie/sub_array_min/SubArrayMin.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
56
src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java
Normal file
56
src/com/github/btrekkie/sub_array_min/SubArrayMinNode.java
Normal file
@@ -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<SubArrayMinNode> {
|
||||
/** 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ public class TreeList<T> extends AbstractList<T> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<T> getNode(int index) {
|
||||
if (index < 0 || index >= root.size) {
|
||||
|
||||
@@ -7,7 +7,7 @@ class TreeListNode<T> extends RedBlackNode<TreeListNode<T>> {
|
||||
/** 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) {
|
||||
|
||||
Reference in New Issue
Block a user