org.anc.util.SkipList Maven / Gradle / Ivy
Show all versions of common Show documentation
/*-
* Copyright 2009 The American National Corpus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.anc.util;
import java.util.Iterator;
/**
* Experimental.
*
* A skip list is a linked list with additonal links that skip over N elements
* in the list. This permits linear searches of the list to skip N nodes when
* looking for the desired node. Once the section of the list containing the
* desired node is located a linear search of those N nodes is performed.
*
*
* Note: only every Nth node contains the "skip link".
*
* @author Keith Suderman
* @version 1.0
*/
public class SkipList> implements Iterable
{
/** Number of nodes each skip link skips over. */
private static final int MAX_SKIP_SIZE = 64;
/** Half of the above. Precalculated to save time. */
private static final int HALF_MAX = 32;
/** The first node in the list. */
private SkipNode fHead = null;
// private SkipNode fTail = null;
/** The number of nodes in the list. */
private long fSize = 0;
public SkipList()
{
}
public long size()
{
return fSize;
}
public void clear()
{
fSize = 0;
fHead.delete();
}
public void add(T item)
{
// assume the insertion always suceeds.
++fSize;
if (fHead == null)
{
fHead = new SkipNode(item);
return;
}
SkipNode block = findBlock(item);
addToBlock(block, item);
}
public void replace(T item)
{
if (fHead == null)
{
add(item);
return;
}
SkipNode node = find(item);
if (node == null)
{
add(item);
}
else
{
node.setItem(item);
}
}
public T get(T item)
{
SkipNode node = find(item);
if (node == null)
{
return null;
}
return node.getItem();
}
public SkipNode find(T item)
{
SkipNode block = findBlock(item);
if (block == null)
{
return null;
}
SkipNode end = block.getNextSkip();
while (block != end)
{
if (item.compareTo(block.getItem()) == 0)
{
return block;
}
block = block.getNext();
}
return null;
}
public void remove(T item)
{
SkipNode node = find(item);
delete(node);
}
public void delete(SkipNode node)
{
if (node == null)
{
return;
}
// System.out.println("Deleting " + node.getItem());
--fSize;
assert (fSize >= 0);
SkipNode prev = node.getPrev();
SkipNode next = node.getNext();
if (node == fHead)
{
// If next is null then we are deleting the last item in the list.
if (next != null)
{
assert (fSize == 0);
next.setPrev(null);
next.setNextSkip(fHead.getNextSkip());
next.setSkipSize(fHead.getSkipSize() - 1);
}
fHead = next;
return;
}
if (prev != null)
{
prev.setNext(next);
}
if (next != null)
{
next.setPrev(prev);
}
// See if this node marks a skip point in the skip list.
next = node.getNextSkip();
prev = node.getPrevSkip();
if (next != null)
{
next.setPrevSkip(prev);
}
if (prev != null)
{
prev.setNextSkip(next);
prev.setSkipSize(prev.getSkipSize() + node.getSkipSize() - 1);
if (prev.getSkipSize() > MAX_SKIP_SIZE)
{
split(prev);
}
}
node = null;
}
@Override
public Iterator iterator()
{
return new SkipListIterator(fHead);
}
/**
* Return the late node such that the item stored at that node is <= the item
* we are looking for. Performs a linear search of the list using the skip
* nodes.
*
* @param item
* T The item being searched for.
* @return SkipNode The last node in the list such that the item in that node
* is <= the item we are looking for.
*/
protected SkipNode findBlock(T item)
{
if (fHead == null)
{
return null;
}
SkipNode block = fHead.getNextSkip();
SkipNode prev = fHead;
while (block != null && item.compareTo(block.getItem()) >= 0)
{
prev = block;
block = block.getNextSkip();
}
// If block == null then we reached the end of the list, otherwise
// block points to the node after the block we are interested in.
return prev;
}
protected void addToBlock(SkipNode block, T item)
{
// addToBlock should not be called on an empty list, that case
// should be handled by the add method directly.
assert (block != null);
// The node we will insert.
SkipNode node = new SkipNode(item);
SkipNode current = block;
SkipNode prev = null;
while (current != null && item.compareTo(current.getItem()) > 0)
{
prev = current;
current = current.next;
}
// The new node should be inserted between the nodes referenced by
// 'prev' and 'current'. If prev == null the new node is the new head
// of the list and if current == null the new node is the new tail of
// the list.
if (prev == null)
{
// But prev has not been set which implies this item is to be added as
// the first item in the block. This also implies that the previous node
// in the skip list needs its skip link updated also.
if (current.getPrev() == null)
{
// this is the new head of the list
node.setNext(fHead);
node.setNextSkip(fHead.getNextSkip());
node.setSkipSize(fHead.getSkipSize() + 1);
fHead.setPrev(node);
fHead.setSkipSize(0);
fHead = node;
if (fHead.getSkipSize() > MAX_SKIP_SIZE)
{
split(fHead);
}
}
else
{
SkipNode prevNode = current.getPrev();
SkipNode prevSkip = current.getPrevSkip();
// This should be the first node in the block, and there should be
// a previous block in the skip list.
assert (prevSkip != null);
prevNode.setNext(node);
prevSkip.setNextSkip(node);
node.setPrev(prevNode);
node.setPrevSkip(prevSkip);
node.setNext(current);
node.setNextSkip(current.getNextSkip());
node.setSkipSize(current.getSkipSize() + 1);
current.setPrev(node);
current.setPrevSkip(null);
current.setSkipSize(0);
if (block.getSkipSize() > MAX_SKIP_SIZE)
{
split(block);
}
}
}
else
{
// This node gets inserted somewhere in the middle of the block.
block.incSkipSize();
prev.setNext(node);
node.setPrev(prev);
node.setNext(current);
if (current != null)
{
current.setPrev(node);
}
if (block.getSkipSize() > MAX_SKIP_SIZE)
{
split(block);
}
}
}
protected void split(SkipNode block)
{
SkipNode nextBlock = block.getNextSkip();
SkipNode midpoint = block;
for (int i = 0; i < HALF_MAX; i++)
{
midpoint = midpoint.getNext();
}
midpoint.setSkipSize(block.getSkipSize() - HALF_MAX);
midpoint.setPrevSkip(block);
midpoint.setNextSkip(nextBlock);
block.setSkipSize(HALF_MAX);
block.setNextSkip(midpoint);
if (nextBlock != null)
{
nextBlock.setPrevSkip(midpoint);
}
}
public void print()
{
Iterator it = iterator();
while (it.hasNext())
{
T i = it.next();
System.out.println(i);
}
System.out.println("Done");
}
public static void main(String[] args)
{
SkipList skiplist = new SkipList();
// for (int i = 0; i < 200 ; ++i )
// {
// skiplist.add(new Integer(i));
// }
// skiplist.print();
//
// for (int i = 0; i < 200; i += 2)
// {
// skiplist.remove(new Integer(i));
// }
skiplist.add(Integer.valueOf(1));
skiplist.add(Integer.valueOf(5));
skiplist.add(Integer.valueOf(2));
skiplist.add(Integer.valueOf(10));
skiplist.add(Integer.valueOf(9));
skiplist.add(Integer.valueOf(5));
skiplist.add(Integer.valueOf(3));
skiplist.add(Integer.valueOf(4));
skiplist.add(Integer.valueOf(6));
skiplist.add(Integer.valueOf(8));
skiplist.add(Integer.valueOf(7));
skiplist.print();
}
public class SkipNode
{
protected SkipNode next = null;
protected SkipNode prev = null;
protected SkipNode nextSkip = null;
protected SkipNode prevSkip = null;
private T1 item;
private int skipSize = 0;
public SkipNode(T1 item)
{
this.item = item;
}
//
// public SkipNode(T item, SkipNode next)
// {
// this(item, next, null, 0);
// }
//
// public SkipNode(T item, SkipNode next, SkipNode skip, int size)
// {
// this.item = item;
// this.next = next;
// this.skip = skip;
// skipSize = size;
// }
public void delete()
{
if (next != null)
{
next.delete();
}
next = prev = nextSkip = prevSkip = null;
}
public SkipNode getNext()
{
return next;
}
public SkipNode getPrev()
{
return prev;
}
public SkipNode getNextSkip()
{
return nextSkip;
}
public SkipNode getPrevSkip()
{
return prevSkip;
}
public T1 getItem()
{
return item;
}
public int getSkipSize()
{
return skipSize;
}
public void incSkipSize()
{
++skipSize;
}
public void setNext(SkipNode next)
{
this.next = next;
}
public void setPrev(SkipNode prev)
{
this.prev = prev;
}
public void setNextSkip(SkipNode next)
{
nextSkip = next;
}
public void setPrevSkip(SkipNode prev)
{
prevSkip = prev;
}
public void setItem(T1 item)
{
this.item = item;
}
public void setSkipSize(int size)
{
this.skipSize = size;
}
}
class SkipListIterator implements Iterator
{
private SkipNode iterator;
public SkipListIterator(SkipNode start)
{
iterator = start;
}
@Override
public boolean hasNext()
{
return iterator != null;
}
@Override
public T next()
{
T result = iterator.getItem();
iterator = iterator.getNext();
return result;
}
@Override
public void remove()
{
if (iterator == null)
{
return;
}
SkipNode node = iterator;
iterator = iterator.getNext();
SkipList.this.delete(node);
}
}
}