Files
baritone/src/com/github/btrekkie/tree_list/TreeList.java
Bill Jacobs 82c1fbdc7b Made fixInsertion return the new root
This changes RedBlackNode.fixInsertion to return the root of the resulting tree.
2016-05-26 10:37:25 -07:00

459 lines
15 KiB
Java

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<T> extends AbstractList<T> {
/** The dummy leaf node. */
private final TreeListNode<T> leaf = new TreeListNode<T>(null);
/** The root node of the tree. */
private TreeListNode<T> root;
/** Constructs a new empty TreeList. */
public TreeList() {
root = leaf;
}
/** Constructs a new TreeList containing the specified values, in iteration order. */
public TreeList(Collection<? extends T> values) {
root = createTree(values);
}
/** Returns the root of a perfectly height-balanced tree containing the specified values, in iteration order. */
private TreeListNode<T> createTree(Collection<? extends T> values) {
List<TreeListNode<T>> nodes = new ArrayList<TreeListNode<T>>(values.size());
for (T value : values) {
nodes.add(new TreeListNode<T>(value));
}
return RedBlackNode.<TreeListNode<T>>createTree(nodes, leaf);
}
/**
* Returns the node for get(index). Raises an IndexOutOfBoundsException if "index" is not in the range [0, size()).
*/
private TreeListNode<T> 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<T> 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<T> 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<T> newNode = new TreeListNode<T>(value);
newNode.left = leaf;
newNode.right = leaf;
if (root.isLeaf()) {
root = newNode;
newNode.isRed = false;
return;
}
newNode.isRed = true;
if (index < root.size) {
TreeListNode<T> 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<T> node;
node = root.max();
node.right = newNode;
newNode.parent = node;
}
root = newNode.fixInsertion();
}
@Override
public T remove(int index) {
TreeListNode<T> node = getNode(index);
modCount++;
T value = node.value;
root = node.remove();
return value;
}
@Override
public boolean addAll(int index, Collection<? extends T> 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<T>[] split = root.split(getNode(index));
root = split[0].concatenate(createTree(values)).concatenate(split[1]);
}
return true;
}
}
@Override
public boolean addAll(Collection<? extends T> 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<T> last;
if (endIndex == root.size) {
last = leaf;
} else {
TreeListNode<T>[] split = root.split(getNode(endIndex));
root = split[0];
last = split[1];
}
TreeListNode<T> 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<T> {
/** 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<T> node;
/** The node containing next(). This is null if we have reached the end of the list. */
private TreeListNode<T> 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<T> iterator() {
return new TreeListIterator();
}
/** The class for TreeList.listIterator. */
private class TreeListListIterator implements ListIterator<T> {
/** 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<T> nextNode;
/** The node for previous(), or null if hasPrevious() is false. */
private TreeListNode<T> 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<T> newNode = new TreeListNode<T>(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<T> predecessor = prevNode.predecessor();
root = prevNode.remove();
prevNode = predecessor;
} else {
TreeListNode<T> successor = nextNode.successor();
root = nextNode.remove();
nextNode = successor;
}
haveModified = true;
TreeList.this.modCount++;
modCount = TreeList.this.modCount;
}
}
@Override
public ListIterator<T> 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);
}
}