Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does java have a skip list implementation

I find ConcurrentSkipListSet in Java Collection Framework, which is backed up with a skip list. But is there a skip list in Java? A set does not work in my use case. I need a indexable list that supports duplicates.

like image 624
Wei Shi Avatar asked Jul 28 '11 19:07

Wei Shi


People also ask

What is skip list in Java?

The skip list is used to store a sorted list of elements or data with a linked list. It allows the process of the elements or data to view efficiently. In one single step, it skips several elements of the entire list, which is why it is known as a skip list. The skip list is an extended version of the linked list.

Where are skip lists used?

First, as mentioned earlier, skip list can be used as the underlying storage for a sorted set data structure. But, skip list can be directly used to implement some operations that are not efficient on a typical sorted set: Find the element in the set that is closest to some given value, in O(log N) time.


7 Answers

This answer is 3 years late but I hope it will be useful for those wanting a Java skip list from this moment on :)

This solution allows duplicates as you asked. I follow roughly the guide here http://igoro.com/archive/skip-lists-are-fascinating, so the complexities are similar to that, except delete costs O(nlogn) - as I didn't bother using doubly-linked nodes, I imagine doing so would bring delete down to O(logn).

Code comprises of: an interface, the skip list implementing the interface, and the node class. It is also generic.

You can tune the parameter LEVELS for performance, but remember the space-time tradeoff.

import java.util.Random;

interface SkippableList<T extends Comparable<? super T>> {
    int LEVELS = 5;

    boolean delete(T target);
    void print();
    void insert(T data);
    SkipNode<T> search(T data);
}

public class SkipList<T extends Comparable<? super T>> implements SkippableList<T> {

    public static void main(String[] args) {
        SkipList<Integer> sl = new SkipList<>();
        int[] data = {4,2,7,0,9,1,3,7,3,4,5,6,0,2,8};
        for (int i : data) {
            sl.insert(i);
        }

        sl.print();
        sl.search(4);

        sl.delete(9);
        sl.print();

        sl.insert(69);
        sl.print();
        sl.search(69);
    }

    private final SkipNode<T> head = new SkipNode<>(null);
    private final Random rand = new Random();

    @Override
    public void insert(T data) {
        SkipNode<T> SkipNode = new SkipNode<>(data);
        for (int i = 0; i < LEVELS; i++) {
            if (rand.nextInt((int) Math.pow(2, i)) == 0) { //insert with prob = 1/(2^i)
                insert(SkipNode, i);
            }
        }
    }

    @Override
    public boolean delete(T target) {
        System.out.println("Deleting " + target.toString());
        SkipNode<T> victim = search(target, false);
        if (victim == null) return false;
        victim.data = null;

        for (int i = 0; i < LEVELS; i++) {
            head.refreshAfterDelete(i);
        }

        System.out.println();
        return true;
    }

    @Override
    public SkipNode<T> search(T data) {
        return search(data, true);
    }

    @Override
    public void print() {
        for (int i = 0; i < LEVELS; i++) {
            head.print(i);
        }

        System.out.println();
    }

    private void insert(SkipNode<T> SkipNode, int level) {
        head.insert(SkipNode, level);
    }

    private SkipNode<T> search(T data, boolean print) {
        SkipNode<T> result = null;
        for (int i = LEVELS-1; i >= 0; i--) {
            if ((result = head.search(data, i, print)) != null) {
                if (print) {
                    System.out.println("Found " + data.toString() + " at level " + i + ", so stoppped" );
                    System.out.println();
                }

                break;
            }
        }

        return result;
    }

}

class SkipNode<N extends Comparable<? super N>> {

    N data;
    @SuppressWarnings("unchecked")
    SkipNode<N>[] next = (SkipNode<N>[]) new SkipNode[SkippableList.LEVELS];

    SkipNode(N data) {
        this.data = data;
    }

    void refreshAfterDelete(int level) {
        SkipNode<N> current = this.getNext(level);
        while (current != null && current.getNext(level) != null) {
            if (current.getNext(level).data == null) {
                SkipNode<N> successor = current.getNext(level).getNext(level);
                current.setNext(successor, level);
                return;
            }

            current = current.getNext(level);
        }
    }

    void setNext(SkipNode<N> next, int level) {
        this.next[level] = next;
    }

    SkipNode<N> getNext(int level) {
        return this.next[level];
    }

    SkipNode<N> search(N data, int level, boolean print) {
        if (print) {
            System.out.print("Searching for: " + data + " at ");
            print(level);
        }

        SkipNode<N> result = null;
        SkipNode<N> current = this.getNext(level);
        while (current != null && current.data.compareTo(data) < 1) {
            if (current.data.equals(data)) {
                result = current;
                break;
            }

            current = current.getNext(level);
        }

        return result;
    }

    void insert(SkipNode<N> SkipNode, int level) {
        SkipNode<N> current = this.getNext(level);
        if (current == null) {
            this.setNext(SkipNode, level);
            return;
        }

        if (SkipNode.data.compareTo(current.data) < 1) {
            this.setNext(SkipNode, level);
            SkipNode.setNext(current, level);
            return;
        }

        while (current.getNext(level) != null && current.data.compareTo(SkipNode.data) < 1 && 
                current.getNext(level).data.compareTo(SkipNode.data) < 1) {

            current = current.getNext(level);
        }

        SkipNode<N> successor = current.getNext(level);
        current.setNext(SkipNode, level);
        SkipNode.setNext(successor, level);
    }

    void print(int level) {
        System.out.print("level " + level + ": [");
        int length = 0;
        SkipNode<N> current = this.getNext(level);
        while (current != null) {
            length++;
            System.out.print(current.data.toString() + " ");
            current = current.getNext(level);
        }

        System.out.println("], length: " + length);
    }

}
like image 92
PoweredByRice Avatar answered Oct 10 '22 04:10

PoweredByRice


Fixed the bug in the implementation provided by @PoweredByRice. It threw an NPE for cases when the node deleted was the first node. Other updates include renamed variable names and reverse printing the order of the skip list.

import java.util.Random;

interface SkippableList<T extends Comparable<? super T>> {

  int LEVELS = 5;

  boolean delete(T target);
  void print();
  void insert(T data);
  SkipNode<T> search(T data);
}

class SkipNode<N extends Comparable<? super N>> {

  N data;
  @SuppressWarnings("unchecked")
  SkipNode<N>[] next = (SkipNode<N>[]) new SkipNode[SkippableList.LEVELS];

  SkipNode(N data) {
    this.data = data;
  }

  void refreshAfterDelete(int level) {
    SkipNode<N> current = this;
    while (current != null && current.getNext(level) != null) {
      if (current.getNext(level).data == null) {
        SkipNode<N> successor = current.getNext(level).getNext(level);
        current.setNext(successor, level);
        return;
      }

      current = current.getNext(level);
    }
  }

  void setNext(SkipNode<N> next, int level) {
    this.next[level] = next;
  }

  SkipNode<N> getNext(int level) {
    return this.next[level];
  }

  SkipNode<N> search(N data, int level, boolean print) {
    if (print) {
      System.out.print("Searching for: " + data + " at ");
      print(level);
    }

    SkipNode<N> result = null;
    SkipNode<N> current = this.getNext(level);
    while (current != null && current.data.compareTo(data) < 1) {
      if (current.data.equals(data)) {
        result = current;
        break;
      }

      current = current.getNext(level);
    }

    return result;
  }

  void insert(SkipNode<N> skipNode, int level) {
    SkipNode<N> current = this.getNext(level);
    if (current == null) {
      this.setNext(skipNode, level);
      return;
    }

    if (skipNode.data.compareTo(current.data) < 1) {
      this.setNext(skipNode, level);
      skipNode.setNext(current, level);
      return;
    }

    while (current.getNext(level) != null && current.data.compareTo(skipNode.data) < 1 &&
      current.getNext(level).data.compareTo(skipNode.data) < 1) {

      current = current.getNext(level);
    }

    SkipNode<N> successor = current.getNext(level);
    current.setNext(skipNode, level);
    skipNode.setNext(successor, level);
  }

  void print(int level) {
    System.out.print("level " + level + ": [ ");
    int length = 0;
    SkipNode<N> current = this.getNext(level);
    while (current != null) {
      length++;
      System.out.print(current.data + " ");
      current = current.getNext(level);
    }

    System.out.println("], length: " + length);
  }

}

public class SkipList<T extends Comparable<? super T>> implements SkippableList<T> {

  private final SkipNode<T> head = new SkipNode<>(null);
  private final Random rand = new Random();

  @Override
  public void insert(T data) {
    SkipNode<T> skipNode = new SkipNode<>(data);
    for (int i = 0; i < LEVELS; i++) {
      if (rand.nextInt((int) Math.pow(2, i)) == 0) {
        //insert with prob = 1/(2^i)
        insert(skipNode, i);
      }
    }
  }

  @Override
  public boolean delete(T target) {
    System.out.println("Deleting " + target);
    SkipNode<T> victim = search(target, true);
    if (victim == null) return false;
    victim.data = null;

    for (int i = 0; i < LEVELS; i++) {
      head.refreshAfterDelete(i);
    }

    System.out.println("deleted...");
    return true;
  }

  @Override
  public SkipNode<T> search(T data) {
    return search(data, true);
  }

  @Override
  public void print() {
    for (int i = LEVELS-1; i >= 0 ; i--) {
      head.print(i);
    }
    System.out.println();
  }

  private void insert(SkipNode<T> SkipNode, int level) {
    head.insert(SkipNode, level);
  }

  private SkipNode<T> search(T data, boolean print) {
    SkipNode<T> result = null;
    for (int i = LEVELS-1; i >= 0; i--) {
      if ((result = head.search(data, i, print)) != null) {
        if (print) {
          System.out.println("Found " + data.toString() + " at level " + i + ", so stopped" );
          System.out.println();
        }
        break;
      }
    }

    return result;
  }

  public static void main(String[] args) {
    SkipList<Integer> sl = new SkipList<>();
    int[] data = {4,2,7,0,9,1,3,7,3,4,5,6,0,2,8};
    for (int i : data) {
      sl.insert(i);
    }

    sl.print();
    sl.search(4);

    sl.delete(4);

    System.out.println("Inserting 10");
    sl.insert(10);
    sl.print();
    sl.search(10);
  }

}
like image 36
Neo Avatar answered Oct 10 '22 05:10

Neo


Since you've mentioned a List that is both Indexable (I assume you want speedy retrieval) and need to allow duplicates, I would advise you go for a custom Set with a LinkedList or ArrayList perhaps.

You need to have a base Set, an HashSet for example and keep adding values to it. If you face a duplicate, the value of that Set should point to a List. So, that you will have both Speedy retrieval and of course you will store your objects in a psuedo Collection manner.

This should give you good efficiency for retrieval. Ideally if your Keys are not duplicates, you will achieve an O(1) as the retrieval speed.

like image 21
bragboy Avatar answered Oct 10 '22 03:10

bragboy


When you create a ConcurrentSkipListSet, you pass a comparator to the constructor.

new ConcurrentSkipListSet<>(new ExampleComparator());

public class ExampleComparator implements Comparator<Event> {//your impl }

You could create a comparator that will make your SkipListSet behave as a normal List.

like image 22
tomekl007 Avatar answered Oct 10 '22 03:10

tomekl007


You can make use the below to code make your own basic skiplist :

1)Make start and end to represent start and end of skip list.

2)Add the nodes and assign pointers to next based on

if(node is even)
    then ,assign a fast lane pointer with next pointer
  else
    assign only pointer to next node

Java code for basic skip list (you can add more features if you want):

public class MyClass {

    public static void main(String args[]) {
        Skiplist skiplist=new Skiplist();

        Node n1=new Node();
        Node n2=new Node();
        Node n3=new Node();
        Node n4=new Node();
        Node n5=new Node();
        Node n6=new Node();

        n1.setData(1);
        n2.setData(2);
        n3.setData(3);
        n4.setData(4);
        n5.setData(5);
        n6.setData(6);

        skiplist.insert(n1);
        skiplist.insert(n2);
        skiplist.insert(n3);
        skiplist.insert(n4);
        skiplist.insert(n5);
        skiplist.insert(n6);

        /*print all nodes*/
        skiplist.display();
        System.out.println();
        /* print only fast lane node*/
        skiplist.displayFast();
    }
}



class Node{
    private int data;
    private Node one_next;  //contain pointer to next node
    private Node two_next;  //pointer to node after the very next node
    public int getData() {
        return data;
    }
    public void setData(int data) {
        this.data = data;
    }
    public Node getOne_next() {
        return one_next;
    }
    public void setOne_next(Node one_next) {
        this.one_next = one_next;
    }
    public Node getTwo_next() {
        return two_next;
    }
    public void setTwo_next(Node two_next) {
        this.two_next = two_next;
    }


}

class Skiplist{
    Node start;      //start pointer to skip list
    Node head;   
    Node temp_next;  //pointer to store last used fast lane node
    Node end;        //end of skip list
    int length;

    public Skiplist(){
        start=new Node();
        end=new Node();
        length=0;
        temp_next=start;
    }

    public void insert(Node node){
        /*if skip list is empty */
        if(length==0){        
            start.setOne_next(node);
            node.setOne_next(end);
            temp_next.setTwo_next(end);
            head=start;
            length++;
        }
        else{
            length++;
            Node temp=start.getOne_next();
            Node prev=start;

            while(temp != end){
                prev=temp;
                temp=temp.getOne_next();
            }

            /*add a fast lane pointer for even no of nodes*/
            if(length%2==0){
                prev.setOne_next(node);
                node.setOne_next(end);
                temp_next.setTwo_next(node);
                temp_next=node;
                node.setTwo_next(end);
            }
            /*odd no of node will not contain fast lane pointer*/
            else{
                prev.setOne_next(node);
                node.setOne_next(end);
            }


        }
    }

    public void display(){
        System.out.println("--Simple Traversal--");
        Node temp=start.getOne_next();

        while(temp != end){
            System.out.print(temp.getData()+"=>");
            temp=temp.getOne_next();

        }
    }

    public void displayFast(){
        System.out.println("--Fast Lane Traversal--");
        Node temp=start.getTwo_next();
        while(temp !=end){
            System.out.print(temp.getData()+"==>");
            temp=temp.getTwo_next();
        }
    }
}

Output:

--Simple Traversal--

1=>2=>3=>4=>5=>6=>

--Fast Lane Traversal--

2==>4==>6==>

like image 34
Prateek Joshi Avatar answered Oct 10 '22 05:10

Prateek Joshi


I approve to use TreeList from apache-collections and decorate it with SortedList from Happy Java Libraries https://sourceforge.net/p/happy-guys/wiki/Sorted%20List/

like image 45
thinker Avatar answered Oct 10 '22 03:10

thinker


I am not claiming that this is my own implementation. I just cannot remember where I found it. If you know let me know and I will update. This has been working quite well for me:

public class SkipList<T extends Comparable<? super T>> implements Iterable<T> {

Node<T> _head = new Node<>(null, 33);
private final Random rand = new Random();
private int _levels = 1;
private AtomicInteger size = new AtomicInteger(0);

/// <summary>
/// Inserts a value into the skip list.
/// </summary>
public void insert(T value) {
    // Determine the level of the new node. Generate a random number R. The
    // number of
    // 1-bits before we encounter the first 0-bit is the level of the node.
    // Since R is
    // 32-bit, the level can be at most 32.
    int level = 0;
    size.incrementAndGet();
    for (int R = rand.nextInt(); (R & 1) == 1; R >>= 1) {
        level++;
        if (level == _levels) {
            _levels++;
            break;
        }
    }

    // Insert this node into the skip list
    Node<T> newNode = new Node<>(value, level + 1);
    Node<T> cur = _head;
    for (int i = _levels - 1; i >= 0; i--) {
        for (; cur.next[i] != null; cur = cur.next[i]) {
            if (cur.next[i].getValue().compareTo(value) > 0)
                break;
        }

        if (i <= level) {
            newNode.next[i] = cur.next[i];
            cur.next[i] = newNode;
        }
    }
}

/// <summary>
/// Returns whether a particular value already exists in the skip list
/// </summary>
public boolean contains(T value) {
    Node<T> cur = _head;
    for (int i = _levels - 1; i >= 0; i--) {
        for (; cur.next[i] != null; cur = cur.next[i]) {
            if (cur.next[i].getValue().compareTo(value) > 0)
                break;
            if (cur.next[i].getValue().compareTo(value) == 0)
                return true;
        }
    }
    return false;
}

/// <summary>
/// Attempts to remove one occurence of a particular value from the skip
/// list. Returns
/// whether the value was found in the skip list.
/// </summary>
public boolean remove(T value) {

    Node<T> cur = _head;

    boolean found = false;
    for (int i = _levels - 1; i >= 0; i--) {
        for (; cur.next[i] != null; cur = cur.next[i]) {
            if (cur.next[i].getValue().compareTo(value) == 0) {
                found = true;
                cur.next[i] = cur.next[i].next[i];
                break;
            }

            if (cur.next[i].getValue().compareTo(value) > 0)
                break;
        }
    }
    if (found)
        size.decrementAndGet();
    return found;
}

@SuppressWarnings({ "rawtypes", "unchecked" })
@Override
public Iterator<T> iterator() {
    return new SkipListIterator(this, 0);
}

public int size() {
    return size.get();
}

public Double[] toArray() {
    Double[] a = new Double[size.get()];
    int i = 0;
    for (T t : this) {
        a[i] = (Double) t;
        i++;
    }
    return a;
  }

 }

 class Node<N extends Comparable<? super N>> {
public Node<N>[] next;
public N value;

@SuppressWarnings("unchecked")
public Node(N value, int level) {
    this.value = value;
    next = new Node[level];
}

public N getValue() {
    return value;
}

public Node<N>[] getNext() {
    return next;
}

public Node<N> getNext(int level) {
    return next[level];
}

public void setNext(Node<N>[] next) {
    this.next = next;
}
}

class SkipListIterator<E extends Comparable<E>> implements Iterator<E> {
   SkipList<E> list;
   Node<E> current;
   int level;

public SkipListIterator(SkipList<E> list, int level) {
    this.list = list;
    this.current = list._head;
    this.level = level;
}

public boolean hasNext() {
    return current.getNext(level) != null;
}

public E next() {
    current = current.getNext(level);
    return current.getValue();
}

public void remove() throws UnsupportedOperationException {
    throw new UnsupportedOperationException();
}
}
like image 22
idipous Avatar answered Oct 10 '22 05:10

idipous