/*
* Author: tdanford
* Date: May 1, 2008
*/
package org.seqcode.gseutils.datastructures;
import java.util.LinkedList;
import java.io.*;
/**
* BinarySplayTree
* @author tdanford
*
* BinarySplayTree is an implementation of binary search trees with the self-adjusting
* "splay" maneuver -- these are commonly known as "splay trees."
*
* This was originally outlined in the paper:
* Sleator and Tarjan, "Self-Adjusting Binary Search Trees" (JACM V.32 no.3, July 1985)
*
* All "quoted" comments to the methods in this class are direct quotes taken from that
* original paper.
*
* @param <X>
*/
public class BinarySplayTree<X extends Comparable<X>>{
private X value;
private BinarySplayTree<X> left, right, parent;
public BinarySplayTree(X v) {
value = v;
parent = left = right = null;
}
public BinarySplayTree(X v, BinarySplayTree<X> l, BinarySplayTree<X> r) {
value = v;
parent = null;
left = l;
right = r;
}
public BinarySplayTree(X v, BinarySplayTree<X> l, BinarySplayTree<X> r, BinarySplayTree<X> p) {
value = v;
left = l;
right = r;
parent = p;
}
public boolean isRoot() { return parent==null; }
public boolean isLeaf() { return left == null && right == null; }
public X getValue() { return value; }
public BinarySplayTree<X> getLeft() { return left; }
public BinarySplayTree<X> getRight() { return right; }
public boolean isLeftChild() {
return !isRoot() && isLeftOf(parent);
}
public boolean isRightChild() {
return !isRoot() && isRightOf(parent);
}
public boolean isLeftOf(BinarySplayTree<X> pt) {
return pt != null && pt==parent && pt.left==this;
}
public boolean isRightOf(BinarySplayTree<X> pt) {
return pt != null && pt==parent && pt.right==this;
}
public void printTree() {
printTree(System.out);
}
public void printTree(PrintStream ps) {
printTree(ps, 0);
}
private void printTree(PrintStream ps, int indent) {
ps.println(value.toString());
if(left != null) {
printIndent(ps, indent+1);
ps.print("-");
left.printTree(ps, indent+1);
}
if(right != null) {
printIndent(ps, indent+1);
ps.print("+");
right.printTree(ps, indent+1);
}
}
private void printIndent(PrintStream ps, int indent) {
for(int i = 0; i < indent; i++) {
ps.print(" ");
}
}
public int childIndicator() {
if(isRightChild()) {
return 1;
} else if (isLeftChild()) {
return -1;
} else {
return 0;
}
}
public BinarySplayTree<X> search(X v) {
BinarySplayTree<X> t = findLast(v);
return t.value.equals(v) ? t : null;
}
private BinarySplayTree<X> findLast(X v) {
int c = v.compareTo(value);
if(c == 0) {
return this;
} else if (c < 0) {
return left != null ? left.search(v) : this;
} else {
return right != null ? right.search(v) : this;
}
}
private LinkedList<BinarySplayTree<X>> stubPath() {
LinkedList<BinarySplayTree<X>> lst = new LinkedList<BinarySplayTree<X>>();
lst.addLast(this);
return lst;
}
public LinkedList<BinarySplayTree<X>> pathingSearch(X v) {
int c = v.compareTo(value);
if(c == 0) {
return stubPath();
} else if (c < 0) {
if(left != null) {
LinkedList<BinarySplayTree<X>> path = left.pathingSearch(v);
path.addFirst(this);
return path;
} else {
return stubPath();
}
} else {
if(right != null) {
LinkedList<BinarySplayTree<X>> path = right.pathingSearch(v);
path.addFirst(this);
return path;
} else {
return stubPath();
}
}
}
public BinarySplayTree<X> findRoot() {
return isRoot() ? this : parent.findRoot();
}
public BinarySplayTree<X> findLargestLeaf() {
if(right == null) {
return this;
} else {
return right.findLargestLeaf();
}
}
/**
* "To perform access(i, t), we search down from the root of t, looking for i.
* If the search reaches node x containing i, we complete the access by splaying
* at x and returning a pointer to x. If the search reaches a null node, indicating
* that i is not in the tree, we complete the access by splaying at the last
* nonnull node reached during the search (the node from which the search ran off
* the bottom of the tree) and returning a pointer to null. If the tree is empty,
* we omit the splaying operation."
*
* @param i
* @return
*/
public boolean access(X i) {
BinarySplayTree<X> leaf = findLast(i);
leaf.splay();
return value.equals(i);
}
/**
* "To carry out insert(i, t), we perform split(i, t) and then replace t
* by a tree consisting of a new root node containing i, whose left and right
* subtrees are the trees t1 and t2 returned by the split."
*
* @param i
*/
public void insert(X i) {
BinarySplayTree<X> other = split(i);
BinarySplayTree<X> newLeft = new BinarySplayTree(value, left, right);
value = i;
left = newLeft; newLeft.parent = this;
right = other; right.parent = this;
}
/**
* "To carry out delete(i, t), we perform access(i, t) and then replace t by
* the join of its left and right subtrees."
*
* @param i
*/
public BinarySplayTree<X> delete(X i) {
access(i);
left.parent = right.parent = null;
left.join(right);
return left;
}
/**
* "To carry out join(t1, t2), we begin by accessing the largest item, say
* i, in t1. After the access, the root of t1 contains i and thus has a
* null right child. We complete the join by making t2 the right subtree
* of this root and returning the resulting tree."
*
* "In both join and split, we must deal specially with the case of an empty
* input tree (or trees)."
*
* @param t2
*/
public void join(BinarySplayTree<X> t2) {
if(!isRoot()) { throw new IllegalArgumentException(); }
if(!t2.isRoot()) { throw new IllegalArgumentException(); }
BinarySplayTree<X> t1Largest = findLargestLeaf();
BinarySplayTree<X> newRoot = t1Largest.splay();
if(newRoot.right != null) { throw new IllegalStateException(); }
newRoot.right = t2;
t2.parent = newRoot;
}
/**
* "To carry out split(i, t) we perform acces(i, t) and then return the
* two trees formed by breaking either the left link or the right link from
* the new root of t, depending on whether the root contains an item greater
* than i or not greater than i."
*
* "In both join and split, we must deal specially with the case of an empty
* input tree (or trees)."
*
* @param i
* @return
*/
public BinarySplayTree<X> split(X i) {
if(!isRoot()) { throw new IllegalArgumentException("Can't split() on a non-root node."); }
BinarySplayTree<X> last = findLast(i);
last.splay();
BinarySplayTree<X> other = null;
if(value.compareTo(i) == 1) {
// break left link
other = left;
left = null;
other.parent = null;
} else {
// break right link
other = right;
right = null;
other.parent = null;
}
return other;
}
/**
* "Splaying step:
* Case 1 (zig): If p(x), the parent of x, is the tree root, rotate the edge
* joining x with p(x). (This case is terminal.)
*
* Case 2 (zig-zig): If p(x) is not the root and x and p(x) are both left or
* right children, rotate the edge joining p(x) with its grandparent g(x) and
* then rotate the edge joining x with p(x).
*
* Case 3 (zig-zag): If p(x) is not the root and x is the left child and p(x) is
* the right child (or vice-versa), rotate the edge joining x with p(x), and then
* rotate the edge joining x with the new p(x)."
*
* @return
*/
public BinarySplayTree<X> splay() {
if(!isRoot()) {
if(parent.isRoot()) {
rotate();
return parent;
} else if (childIndicator() == parent.childIndicator()) {
parent.rotate();
rotate();
return parent.parent.splay();
} else {
rotate();
rotate();
return parent.parent.splay();
}
}
return this;
}
public void rotate() {
if(isLeftChild()) {
rotateRight();
} else if (isRightChild()) {
rotateLeft();
}
}
public void rotateRight() {
if(isRoot()) { throw new IllegalArgumentException("Can't rotate right on root."); }
if(!isLeftOf(parent)) { throw new IllegalArgumentException("Right child can't rotate right."); }
X temp = value;
parent.left = left; left.parent = parent;
value = parent.value;
left = right;
right = parent.right; right.parent = this;
parent.value = temp;
parent.right = this;
}
public void rotateLeft() {
if(isRoot()) { throw new IllegalArgumentException("Can't rotate left on root."); }
if(!isRightOf(parent)) { throw new IllegalArgumentException("Left child can't rotate left."); }
X temp = value;
parent.right = right; right.parent = parent;
value = parent.value;
right = left;
left = parent.left; left.parent = this;
parent.value = temp;
parent.left= this;
}
}