import java.util.Random; /** * This class implements the {@link Map}-interface as skiplists. In * a skiplist each key-value element is inserted into the n lowest * lists. Here, n is determined randomly in a geometric distribution. * So, given a propability p with 0 < p < 1 a new key-value * element is inserted into n lists with propability * p * (1-p)n-1. * * All standard operations like insertion, deletion, finding, * etc. need O(log n) time in average. * * @author Rossmanith * * @param The type of the key. Must be comparable. * @param The type of the data. * @see Comparable */ public class Skiplist,D> extends Dictionary { /** * The size of the list, i.e., the number of keys. */ int size; /** * This attribute stores the propability p as mentioned in the * destriction of the {@link Skiplist}-class. */ double prob = 0.5; /** * The random nr generator. */ Random rand; /** * The class for the inner nodes of a skiplist. Just * stores the key, the data, and the an array with * the successors of that node. */ /* * Note that only the succ-array of a skiplist-head * will change its size, but the array of all other * nodes will keep its size. */ class Node { Array succ; K key; D data; } /* * Head and tail of the skiplist, won't be changed. */ Node head, tail; /** * Creates a new empty skiplist with * default geometric distribution (where p = 0.5). */ public Skiplist() { /* * create head and tail and add one link * on level 0 from head to tail */ head = new Node(); tail = new Node(); head.succ = new Array(); tail.succ = new Array(); head.succ.set(0, tail); size = 0; rand = new Random(); } /** * Creates a new skiplist with given random seed. * @param r The seed that is used to initialize the random number * generator for the created skiplist. */ public Skiplist(int r) { /* * first create the list with this() and then overwrite the random * generator. */ this(); rand = new Random(r); } /** * Creates a new skiplist with given random seed and * given probability p. * @param r The seed that is used to initialize the random number * generator for the created skiplist. * @param p The probability p as mentioned in the desription of the {@link Skiplist}-class. */ public Skiplist(int r, double p) { /* * first create the list with this() and then overwrite the random * generator and the probability value. */ this(); rand = new Random(r); prob = p; } /** * Inserts a the link to the new skiplist node n on the i-th level. * @param i The level to insert the node. * @param m A node at which one can start to search for the * right position for the new node when inserting on the i-th level. * @param n The node to insert. Note that the key of n must not be present at * the i-th level before. * @return A node at which one can start to search for the right position * for the new node on the (i-1)-th level. */ Node insert_on_level(int i, Node m, Node n) { /* * Step forwards with m on the i-th level, * unless the successor of m is the tail or * the successor of m has a higher key than the key of n. */ while(m.succ.get(i)!=tail && m.succ.get(i).key.compareTo(n.key)<0) m=m.succ.get(i); /* * Then insert n right after m on the i-th level. */ n.succ.set(i, m.succ.get(i)); m.succ.set(i, n); return m; } /** * Associates the data d to the key k in this skiplist. * Takes O(log n) time in average. * * @param k The key to store. * @param d The data associated to the key k. * @see Map#insert(Object, Object) */ public void insert(K k, D d) { /* * first delete the old entry, if it exists */ delete(k); /* * then generate the nr of levels * s on which the new node should be inserted */ int s=1; while(rand.nextDouble() >= prob) s++; /* * and create the new node */ Node n = new Node(); n.key = k; n.data = d; n.succ = new Array(s); /* * now adjust the nr of successors of the head * such the head has at least s successors. * This is done by creating new links to the * tail on the missing upper levels. */ Node m = head; for(int i=0; i= head.succ.size()) head.succ.set(i, tail); /* * finally insert the new node on the lower * s levels, starting from the highest. */ for(int i=s-1; i>=0; i--) m = insert_on_level(i, m, n); /* * and finally update the size */ size++; } /** * Looks up the node associated to the key k. Needs * logarithmic time in average. * @param k The key for which the node should be found. * @return null, if k is not contained in this skiplist, * or the internal node with key k, otherwise. */ Node findnode(K k) { /* * start looking at the head, and start * on the highest level, going down to level 0. */ Node n = head; for(int i=head.succ.size()-1; i>=0; i--) /* * step forwards on i-th level until tail is reached or * until the successor has a higher key than k. * Then go down own level and continue search. */ while(n.succ.get(i)!=tail && n.succ.get(i).key.compareTo(k) <= 0) n = n.succ.get(i); /* * finally look whether we have found the key, i.e., * whether in n the key k is stored. */ if(n==head || !n.key.equals(k)) return null; return n; } /** * Looks up the data associated to the key k. Needs * logarithmic time in average. * @param k The key for which the data should be found. * @return null, if there is no data for k, or the * associated data, otherwise. * @see Map#find(Object) */ public D find(K k) { /* * just use the findnode method */ Node n = findnode(k); if(n==null) return null; return n.data; } /** * Checks whether the key is present in this skiplist * (in O(log n) time in average). * @param k The key to look for. * @return true, iff the k is present in this skiplist. * @see Map#iselement(Object) */ public boolean iselement(K k) { /* * just use the findnode method */ return findnode(k)!=null; } /** * Deletes the key-data association for the given key in * this skiplist in O(log n) in average. * @param k The key which should be deleted * @see Map#delete(Object) */ public void delete(K k) { Node n = head; /* * if the key is not present in this list, then do nothing, * otherwise decrease size of this list and continue */ if(iselement(k)) size--; else return; /* * start traversing from head and from the highest level. * Then repeatedly delete on i-th level. */ for(int i=head.succ.size()-1; i>=0; i--) { /* * traverse until the successor of n is the tail or * it has a (non-strictly) higher key than k. */ while(n.succ.get(i)!=tail && n.succ.get(i).key.compareTo(k) < 0) n = n.succ.get(i); /* * if the successor has the key we want to delete, * then delete it from the i-th level */ if(n.succ.get(i)!=tail && n.succ.get(i).key.equals(k)) n.succ.set(i, n.succ.get(i).succ.get(i)); } } /** * Returns the size of this skiplist (in constant time). * @return The size of this skiplist. * @see Map#size() */ public int size() { return size; } /** * Returns whether this skiplist is empty (in constant time). * @return true, if this list is empty, and false, otherwise. * @see Map#isempty() */ public boolean isempty() { return this.size == 0; } public Iterator iterator() { return new Skiplistiterator(); } /** * This class is used to iterate over a skiplist. This * can be done by traversing the lowest level, i.e., level 0. * Then only the current node has to be stored in the * iterator. */ class Skiplistiterator implements Iterator { Node n; public Skiplistiterator() { n = head.succ.get(0); } public K key() { return n.key; } public D data() { return n.data; } public boolean more() { return n!=tail; } public void step() { n = n.succ.get(0); } } public void print() { Node n = head.succ.get(0); while(n!=tail) { System.out.println(n.key); n = n.succ.get(0); } } }