/*
* (c) Nathaniel Lim
* CS 256
* Homework 9, 5/5/09
*/
import java.util.*;
/**
* Skip Lists support the following operations in expected O(log n) time w.h.p:
* Addition
* Removal
* Containment
* Predecessor
* Successor
*
*/
public class SkipList {
private Node root;
private Node lowerLeft;
private Node lowerRight;
private Random rng = new Random(47);
private int size = 0;
public SkipList() {
root = new LeftBoundaryNode();
//All 8 initial linkages
root.right = new RightBoundaryNode();
root.right.left = root;
root.right.down = new RightBoundaryNode();
root.right.down.up = root.right;
root.right.down.left = new LeftBoundaryNode();
root.right.down.left.right = root.right.down;
root.down = root.right.down.left;
root.right.down.left.up = root;
lowerLeft = root.down;
lowerRight = root.right.down;
}
public int size(){
return this.size;
}
public Integer predecessor(int i){
Node current = root;
boolean found = false;
while (!found){
if (isBottom(current)){
if (current.right.value < i &&
!current.right.isRightBoundary()){
current = current.right;
} else{
found = true;
}
} else {
if (current.right.value < i &&
!current.right.isRightBoundary()){
current = current.right;
} else {
current = current.down;
}
}
}
if (current.isLeftBoundary()){
return null;
} else {
return new Integer(current.value);
}
}
public boolean remove(int i){
//Find the node, and traverse to bottom.
Node current = root;
boolean found = false;
while (!found){
if (current.value == i && !current.isLeftBoundary()){
if(isBottom(current)){
found = true;
} else {
current = current.down;
}
} else if (current.right.value <= i && !current.right.isRightBoundary()){
current = current.right;
} else { // if right.value > i || right.isRightBoundary()
if (isBottom(current)){
return false;
} else {
current = current.down;
}
}
}
//Current is now the node on the bottom, remove success levels
Node next;
Node leftBound = lowerLeft;
boolean checkExtraTop = false;
while (current != null){
checkExtraTop = current.left.isLeftBoundary() && current.right.isRightBoundary();
next = current.up;
leftBound = leftBound.up;
//Insert
current.left.right = current.right;
current.right.left = current.left;
current = next;
}
//Remove extra top
if(checkExtraTop){
Node rightBound = leftBound.right;
leftBound.down.up = null;
rightBound.down.up = null;
root = leftBound.down;
}
this.size--;
return true;
}
public boolean contains(int i){
//Find the node, and doesn't traverse to the bottom if found on higher level
Node current = root;
boolean found = false;
while (!found){
if (current.value == i && !current.isLeftBoundary()){
if(isBottom(current)){
found = true;
} else {
current = current.down;
}
} else if (current.right.value <= i && !current.right.isRightBoundary()){
current = current.right;
} else { // if right.value > i || right.isRightBoundary()
if (isBottom(current)){
return false;
} else {
current = current.down;
}
}
}
return true;
}
public Integer successor(int i){
Node current = root;
boolean found = false;
while (!found){
if (isBottom(current)){
if (current.right.value < i &&
!current.right.isRightBoundary()){
current = current.right;
} else{
current = current.right;
found = true;
}
} else {
if (current.right.value < i &&
!current.right.isRightBoundary()){
current = current.right;
} else {
current = current.down;
}
}
}
if (current.isRightBoundary()){
return null;
} else {
return new Integer(current.value);
}
}
public boolean add (int i){
boolean spotFound = false;
Node current = root;
Node insert = new Node();
insert.value = i;
while (!spotFound){
if (isBottom(current)){//If it is farthest to bottom
if (current.right.isRightBoundary() ||
insert.value < current.right.value) {
insert.right = current.right;
insert.left = current;
current.right.left = insert;
current.right = insert;
spotFound = true;
} else if (insert.value == current.right.value){
return false; //The number already exists in the SkipList
}else {
current = current.right;
}
} else { //In the Skip levels (not the bottom)
if (current.right.isRightBoundary() ||
current.right.value > insert.value){
//Skipping forward, passes where the insert should go
current = current.down;
} else if (current.right.value == insert.value){
return false; //The number already exists in the SkipList
} else { //current.right not R and current.right.val < insert.val
current = current.right;
//Skipping makes sense
}
}
} //End the loop, reached the bottom, inserted
// Now go back up
boolean done = false;
Node leftBound = lowerLeft;
Node rightBound = lowerRight;
while (!done){
if (isTop(leftBound)){
//Add an upper boundary
//6 Connections
leftBound.up = new LeftBoundaryNode();
rightBound.up = new RightBoundaryNode();
leftBound.up.down = leftBound;
rightBound.up.down = rightBound;
leftBound.up.right = rightBound.up;
rightBound.up.left = leftBound.up;
root = leftBound.up;
done = true;
} else {
if (rng.nextBoolean()){//flip a coin
//Promote insert to another level
leftBound = leftBound.up;
rightBound = rightBound.up;
current = leftBound;
while (!current.right.isRightBoundary() &&
insert.value >current.right.value){
current = current.right;
//Traverse until the right is a boundary or less than insert.value
}
Node insert2 = new Node();
insert2.value = insert.value;//Clone the insert node;
insert2.right = current.right;
insert2.left = current;
current.right.left = insert2;
current.right = insert2;
insert.up = insert2;
insert2.down = insert;
insert = insert2;
} else {
done = true;
}
}
}
this.size++;
return true;
}
public boolean isBottom(Node n){
return n.down == null;
}
public boolean isTop(Node leftBound){
if (leftBound==null){
return false;
}
if (!leftBound.isLeftBoundary()){
return false; //Should never happen when I call it
}
return leftBound.up == null;
}
public Node findLeft(Node n){
Node temp = n;
while (!temp.isLeftBoundary()){
temp = temp.left;
}
return temp;
}
public Node findRight(Node n){
Node temp = n;
while (!temp.isRightBoundary()){
temp = temp.right;
}
return temp;
}
public String toString() {
return root.toString();
}
// ------------------------------------------------------
/**
* Inner class representing a non-boundary Node of the skip list
*/
private class Node {
public int value;
public Node left, right, up, down;
public Node() {
left = right = up = down = null;
value = 0;
}
public boolean isLeftBoundary() {
return false;
}
public boolean isRightBoundary() {
return false;
}
/**
* Find the node with the largest value not exceeding i
*/
public Node find(int i) {
return null;
}
/**
* A simple string representation of the node. Null Values don't appear
*/
public String toString() {
String s = "[" + this.value + " ";
if (up != null) {
s = s + "U: " + up.value + " ";
}
if (down != null) {
s = s + "D: " + down.value + " ";
}
if (left != null) {
s = s + "L: " + left.value + " ";
}
if (right != null) {
s = s + "R: " + right.value + " ";
}
s = s + "]";
return s;
}
}
/**
* A node representing a left boundary
*/
private class LeftBoundaryNode extends Node {
public boolean isLeftBoundary() {
return true;
}
public String toString() {
String s = super.toString() + " -- ";
Node n = this.right;
while (!n.isRightBoundary()) {
s = s + n.toString() + " -- ";
n = n.right;
}
s = s + n.toString() + "\n";
if (this.down != null) {
s = s + this.down.toString();
}
return s;
}
}
/**
* A node representing a right boundary
*/
private class RightBoundaryNode extends Node {
public boolean isRightBoundary() {
return true;
}
}
}