Created
December 7, 2012 00:47
-
-
Save isaacsanders/4229801 to your computer and use it in GitHub Desktop.
This is my eternal shame.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.ConcurrentModificationException; | |
import java.util.Iterator; | |
import java.util.NoSuchElementException; | |
import java.util.Stack; | |
public class AVLTree<T extends Comparable<T>> implements Iterable<T> { | |
public class BoolWrapper { | |
public boolean value; | |
public BoolWrapper(boolean value) { | |
this.value = value; | |
} | |
} | |
public class BinaryNode { | |
public T getElement() { | |
return this.value; | |
} | |
public BinaryNode getRightChild() { | |
return this.rightChild; | |
} | |
public void setRightChild(BinaryNode rightChild) { | |
this.rightChild = rightChild; | |
} | |
public BinaryNode getLeftChild() { | |
return this.leftChild; | |
} | |
public void setLeftChild(BinaryNode leftChild) { | |
this.leftChild = leftChild; | |
} | |
private static final int BALANCE_FACTOR_ON_RIGHT_IMBALANCES = -2; | |
private static final int BALANCE_FACTOR_ON_LEFT_IMBALANCES = 2; | |
private static final String ELEMENT_STRING_DELIMITER = ", "; | |
private BinaryNode rightChild; | |
private BinaryNode leftChild; | |
private T value; | |
private int height = 0; | |
public BinaryNode(T value) { | |
this.value = value; | |
} | |
private int leftHeight() { | |
if (this.hasLeftChild()) { | |
return this.leftChild.height; | |
} else { | |
return -1; | |
} | |
} | |
private int rightHeight() { | |
if (this.hasRightChild()) { | |
return this.rightChild.height; | |
} else { | |
return -1; | |
} | |
} | |
public boolean hasLeftChild() { | |
return this.leftChild != null; | |
} | |
public boolean hasRightChild() { | |
return this.rightChild != null; | |
} | |
@Override | |
public String toString() { | |
return this.leftChildString() + this.value + this.rightChildString(); | |
} | |
private String rightChildString() { | |
if (this.hasRightChild()) { | |
return ELEMENT_STRING_DELIMITER + this.rightChild.toString(); | |
} else { | |
return ""; | |
} | |
} | |
private String leftChildString() { | |
if (this.hasLeftChild()) { | |
return this.leftChild.toString() + ELEMENT_STRING_DELIMITER; | |
} else { | |
return ""; | |
} | |
} | |
public ArrayList<T> toArrayList() { | |
ArrayList<T> arrayList = new ArrayList<T>(); | |
arrayList.add(this.value); | |
if (this.hasLeftChild()) { | |
for(T value : this.leftChild.toArrayList()) { | |
arrayList.add(value); | |
} | |
} | |
if (this.hasRightChild()) { | |
for(T value : this.rightChild.toArrayList()) { | |
arrayList.add(value); | |
} | |
} | |
return arrayList; | |
} | |
public int balanceFactor() { | |
return this.leftHeight() - this.rightHeight(); | |
} | |
public BinaryNode insert(T object, BoolWrapper anElementWasInserted) { | |
boolean insertObjectToTheLeft = object.compareTo(this.value) < 0; | |
boolean insertObjectToTheRight = object.compareTo(this.value) > 0; | |
BinaryNode newThis = this; | |
if (insertObjectToTheLeft) { | |
newThis.leftChild = this.insertLeft(object, anElementWasInserted); | |
} else if (insertObjectToTheRight) { | |
newThis.rightChild = this.insertRight(object, anElementWasInserted); | |
} | |
if (anElementWasInserted.value) { | |
newThis.resetHeight(); | |
if (newThis.hasLeftChild()) { | |
newThis.leftChild.resetHeight(); | |
} | |
if (newThis.hasRightChild()) { | |
newThis.rightChild.resetHeight(); | |
} | |
} | |
if (this.balanceFactor() == BALANCE_FACTOR_ON_LEFT_IMBALANCES) { | |
newThis = this.balanceLeft(); | |
} | |
if (this.balanceFactor() == BinaryNode.BALANCE_FACTOR_ON_RIGHT_IMBALANCES) { | |
newThis = this.balanceRight(); | |
} | |
return newThis; | |
} | |
private void resetHeight() { | |
if (this.leftHeight() > this.rightHeight()) { | |
this.height = 1 + this.leftHeight(); | |
} else { | |
this.height = 1 + this.rightHeight(); | |
} | |
} | |
private BinaryNode balanceLeft() { | |
if (this.leftChild.balanceFactor() == -1) { | |
this.leftChild = this.leftChild.performLeftRotation(); | |
} | |
return this.performRightRotation(); | |
} | |
private BinaryNode balanceRight() { | |
if (this.rightChild.balanceFactor() == 1) { | |
this.rightChild = this.rightChild.performRightRotation(); | |
} | |
return this.performLeftRotation(); | |
} | |
private BinaryNode insertLeft(T object, BoolWrapper anElementWasInserted) { | |
if (this.hasLeftChild()) { | |
return this.leftChild.insert(object, anElementWasInserted); | |
} else { | |
anElementWasInserted.value = true; | |
return new BinaryNode(object); | |
} | |
} | |
private BinaryNode insertRight(T object, BoolWrapper anElementWasInserted) { | |
if (this.hasRightChild()) { | |
return this.rightChild.insert(object, anElementWasInserted); | |
} else { | |
anElementWasInserted.value = true; | |
return new BinaryNode(object); | |
} | |
} | |
public BinaryNode performRightRotation() { | |
AVLTree.this.rotations++; | |
BinaryNode newRight = new BinaryNode(this.value); | |
newRight.rightChild = this.rightChild; | |
if (this.leftChild.hasRightChild()) { | |
newRight.leftChild = this.leftChild.rightChild; | |
} | |
BinaryNode newLeft = this.leftChild.leftChild; | |
BinaryNode node = new BinaryNode(this.leftChild.value); | |
node.rightChild = newRight; | |
node.leftChild = newLeft; | |
node.resetHeight(); | |
return node; | |
} | |
public BinaryNode performLeftRotation() { | |
AVLTree.this.rotations++; | |
BinaryNode newLeft = new BinaryNode(this.value); | |
newLeft.leftChild = this.leftChild; | |
if (this.rightChild.hasLeftChild()) { | |
newLeft.rightChild = this.rightChild.leftChild; | |
} | |
BinaryNode newRight = this.rightChild.rightChild; | |
BinaryNode node = new BinaryNode(this.rightChild.value); | |
node.leftChild = newLeft; | |
node.rightChild = newRight; | |
node.resetHeight(); | |
return node; | |
} | |
private boolean isGreaterValue(T object) { | |
return object.compareTo(this.value) > 0; | |
} | |
private boolean isLesserValue(T object) { | |
return object.compareTo(this.value) < 0; | |
} | |
public BinaryNode remove(T object, BoolWrapper anElementWasRemoved) { | |
BinaryNode newThis = this; | |
if (this.isGreaterValue(object)) { | |
if (this.hasRightChild()) { | |
newThis.rightChild = this.rightChild.remove(object, anElementWasRemoved); | |
} | |
} else if (this.isLesserValue(object)) { | |
if (this.hasLeftChild()) { | |
newThis.leftChild = this.leftChild.remove(object, anElementWasRemoved); | |
} | |
} else { | |
newThis = this.removeSelf(anElementWasRemoved); | |
} | |
if (anElementWasRemoved.value && newThis != null) { | |
newThis.resetHeight(); | |
if (newThis.hasLeftChild()) { | |
newThis.leftChild.resetHeight(); | |
} | |
if (newThis.hasRightChild()) { | |
newThis.rightChild.resetHeight(); | |
} | |
} | |
if (this.balanceFactor() == BALANCE_FACTOR_ON_LEFT_IMBALANCES) { | |
newThis = this.balanceLeft(); | |
} | |
if (this.balanceFactor() == BinaryNode.BALANCE_FACTOR_ON_RIGHT_IMBALANCES) { | |
newThis = this.balanceRight(); | |
} | |
return newThis; | |
} | |
private BinaryNode removeSelf(BoolWrapper anElementWasRemoved) { | |
anElementWasRemoved.value = true; | |
AVLTree.this.modifications++; | |
if (this.hasBothChildren()) { | |
return this.removeFromNodeWithTwoChildren(anElementWasRemoved); | |
} else if (this.hasLeftChild()) { | |
return this.leftChild; | |
} else if (this.hasRightChild()) { | |
return this.rightChild; | |
} else { | |
return null; | |
} | |
} | |
private BinaryNode removeFromNodeWithTwoChildren(BoolWrapper anElementWasRemoved) { | |
BinaryNode greatestNodeInLeftSubTree = this.leftChild; | |
while (greatestNodeInLeftSubTree.hasRightChild()) { | |
greatestNodeInLeftSubTree = greatestNodeInLeftSubTree.rightChild; | |
} | |
this.value = greatestNodeInLeftSubTree.value; | |
this.leftChild = this.leftChild.remove(this.value, anElementWasRemoved); | |
return this; | |
} | |
private boolean hasBothChildren() { | |
return this.hasLeftChild() && this.hasRightChild(); | |
} | |
public T get(T object) { | |
if (this.isGreaterValue(object)) { | |
if (this.hasRightChild()) { | |
return this.rightChild.get(object); | |
} | |
} else if (this.isLesserValue(object)) { | |
if (this.hasLeftChild()) { | |
return this.leftChild.get(object); | |
} | |
} else { | |
return this.value; | |
} | |
return null; | |
} | |
} | |
public class LazyPreOrderTreeIterator implements Iterator<T> { | |
protected int localModifications; | |
protected Stack<BinaryNode> stack; | |
protected BinaryNode lastReturnedNode = null; | |
public LazyPreOrderTreeIterator() { | |
this.stack = new Stack<BinaryNode>(); | |
if (AVLTree.this.hasRoot()) { | |
this.push(AVLTree.this.root); | |
} | |
this.localModifications = AVLTree.this.modifications; | |
} | |
protected void push(BinaryNode node) { | |
this.stack.push(node); | |
} | |
protected BinaryNode pop() { | |
return this.stack.pop(); | |
} | |
@Override | |
public T next() { | |
if (this.localModifications != AVLTree.this.modifications) { | |
throw new ConcurrentModificationException(); | |
} | |
if (this.hasNext()) { | |
this.lastReturnedNode = this.pop(); | |
this.pushRightChild(this.lastReturnedNode); | |
this.pushLeftChild(this.lastReturnedNode); | |
return this.lastReturnedNode.value; | |
} else { | |
throw new NoSuchElementException(); | |
} | |
} | |
protected void pushLeftChild(BinaryNode node) { | |
if (node.hasLeftChild()) { | |
this.push(node.leftChild); | |
} | |
} | |
protected void pushRightChild(BinaryNode node) { | |
if (node.hasRightChild()) { | |
this.push(node.rightChild); | |
} | |
} | |
@Override | |
public void remove() { | |
if (this.lastReturnedNode == null) { | |
throw new IllegalStateException(); | |
} else { | |
T object = this.lastReturnedNode.value; | |
AVLTree<T> tree = AVLTree.this; | |
tree.remove(object); | |
this.lastReturnedNode = null; | |
} | |
} | |
@Override | |
public boolean hasNext() { | |
return !this.stack.empty(); | |
} | |
} | |
private static final String RIGHT_STRING_DELIMITER = "]"; | |
private static final String LEFT_STRING_DELIMITER = "["; | |
private static final String EMPTY_TREE_STRING = LEFT_STRING_DELIMITER + RIGHT_STRING_DELIMITER; | |
public BinaryNode root; | |
private int modifications; | |
private int rotations = 0; | |
public AVLTree(BinaryNode root) { | |
this.root = root; | |
} | |
public AVLTree() {} | |
public int height() { | |
if (this.hasRoot()) { | |
return this.root.height; | |
} else { | |
return -1; | |
} | |
} | |
private boolean hasRoot() { | |
return this.root != null; | |
} | |
public boolean isEmpty() { | |
return !this.hasRoot(); | |
} | |
@Override | |
public String toString() { | |
if (this.hasRoot()) { | |
return LEFT_STRING_DELIMITER + this.root.toString() + RIGHT_STRING_DELIMITER; | |
} else { | |
return EMPTY_TREE_STRING; | |
} | |
} | |
@Override | |
public Iterator<T> iterator() { | |
return new LazyPreOrderTreeIterator(); | |
} | |
public ArrayList<T> toArrayList() { | |
if (this.hasRoot()) { | |
return this.root.toArrayList(); | |
} else { | |
return new ArrayList<T>(); | |
} | |
} | |
public Object[] toArray() { | |
return this.toArrayList().toArray(); | |
} | |
public boolean insert(T object) { | |
if (object == null) { | |
throw new IllegalArgumentException(); | |
} | |
BoolWrapper anElementWasInserted = new BoolWrapper(false); | |
if (this.hasRoot()) { | |
this.root = this.root.insert(object, anElementWasInserted); | |
return anElementWasInserted.value; | |
} else { | |
this.root = new BinaryNode(object); | |
return true; | |
} | |
} | |
public boolean remove(T object) { | |
if (object == null) { | |
throw new IllegalArgumentException(); | |
} | |
BoolWrapper anElementWasRemoved = new BoolWrapper(false); | |
if (this.hasRoot()) { | |
this.root = this.root.remove(object, anElementWasRemoved); | |
} else { | |
return false; | |
} | |
if (anElementWasRemoved.value) { | |
this.modifications++; | |
} | |
return anElementWasRemoved.value; | |
} | |
public T get(T object) { | |
if (this.hasRoot()) { | |
return this.root.get(object); | |
} else { | |
return null; | |
} | |
} | |
public int getRotationCount() { | |
return this.rotations; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Something like this be an improvement?