第一次知道跳表这种数据结构的时间大概是在一年前(说这句话时候可能就被无数同胞鄙视了),但自己却不知道如何实现。当时印象最深的就是这篇文章跳跃表(Skip List)-实现(Java),因为这篇文章中的Skip List的实现方式最让人容易理解,我最初也是通过这篇文章对跳表有了更进一步的认识,所以,真的要在这里感谢这篇文章的主人。一年之后,我发现自己对跳表的认识又模糊不清了,所以最先想到的也是这篇文章。今天再次拜读此文,同时实现了其中未给出的删除方法。并增加了泛型,但泛型也只是对value采用了泛型,对Key依然采用原文中的String类型。所以依然比较简单和局限。之所以贴出来,是为了增进自己对Skip List的理解和作为备忘。原文的链接如之前所述,原文具体作者其实我也不知道是谁,只想在此默默的说声感谢。当然了,若原文作者觉得我有什么冒犯或侵权的行为,我会立马删帖。
关于跳表的定义和介绍,读者可以参考原文。这里就直接给出原码了,这里的原码与原文的唯一的一点区别就是,我这里给出了原文没给出的删除方法(原文其实参考的是一篇英文文章,英文文章给出了删除方法的伪代码,我直到后来才发现,不过自己的实现和和伪代码中的思路是一样的,代码略显多余,此处贴出来的是我自己实现的删除方法)。可能实现上比较糟糕,所以也恳请大家批评指正!!!
1 对跳表中各个元素(键值对)的封装类SkipListEntry.java
public class SkipListEntry<V>
{
public String key;
public V value;
public int pos;
public SkipListEntry<V> up, down, left, right;
public static String negInf = new String("-oo");
public static String posInf = new String("+oo");
public SkipListEntry(String k, V v)
{
key = k;
value = v;
up = down = left = right = null;
}
public V getValue()
{
return value;
}
public String getKey()
{
return key;
}
public V setValue(V val)
{
V oldValue = value;
value = val;
return oldValue;
}
@SuppressWarnings("unchecked")
public boolean equals(Object o)
{
SkipListEntry<V> entry;
try
{
entry = (SkipListEntry<V>) o;
} catch (ClassCastException ex)
{
return false;
}
return (entry.getKey() == key) && (entry.getValue().equals(value));
}
public String toString()
{
return "(" + key + "," + value + ")";
}
}
2 Skip List的具体实现(包含增、删、改、查 )
import java.util.Random;
/**
* 跳表的一种简单实现。key只能为字符串类型,value可以为任意对象类型
* @param <V>
* @author xxx 2017年2月14日 下午9:42:06
* @version v1.0
*/
public class SkipList<V>
{
public SkipListEntry<V> head;
public SkipListEntry<V> tail;
public int size;
public int height;
public Random flag;
/**
* 默认构造函数
* @author xxx 2017年2月14日 下午9:32:22
* @since v1.0
*/
public SkipList()
{
head = new SkipListEntry<V>(SkipListEntry.negInf, null);
tail = new SkipListEntry<V>(SkipListEntry.posInf, null);
head.right = tail;
tail.left = head;
size = 0;
height = 0;
flag = new Random();
}
/**
* 返回元素的个数
* @return
* @author xxx 2017年2月14日 下午9:35:22
* @since v1.0
*/
public int size()
{
return size;
}
/**
* 判断跳表中的元素个数是否为零
* @return
* @author xxx 2017年2月14日 下午9:35:52
* @since v1.0
*/
public boolean isEmpty()
{
return (size == 0);
}
/**
* 从最顶层的第一个元素,也即head元素开始查找,直到找到第0层、要插入的位置前面的那个key
* @param k
* @return
* @author xxx 2017年2月14日 下午9:42:12
* @since v1.0
*/
private SkipListEntry<V> findEntry(String k)
{
SkipListEntry<V> p = head;
while (true)
{
while (p.right.key != SkipListEntry.posInf && p.right.key.compareTo(k) <= 0)
{
p = p.right;
}
if (p.down != null)
{
p = p.down;
} else
{
break;
}
}
return p;
}
/** 返回和key绑定的值 */
public V get(String k)
{
SkipListEntry<V> p = findEntry(k);
if (k.equals(p.getKey()))
{
return p.value;
} else
{
return null;
}
}
/**
* 往跳表中插入一个键值对,如果键已经存在,则覆盖相应的值并返回旧值
* @param k
* @param v
* @return
* @author xxx 2017年2月14日 下午9:48:54
* @since v1.0
*/
public V put(String k, V v)
{
System.out.println("-----插入[" + k + "]之前的跳跃表是:-----");
printHorizontal();
SkipListEntry<V> p, q;
p = findEntry(k);
if (k.equals(p.getKey()))
{
V old = p.value;
p.value = v;
return old;
}
q = new SkipListEntry<V>(k, v);
q.left = p;
q.right = p.right;
p.right.left = q;
p.right = q;
int currentLevel = 0;
while (flag.nextDouble() < 0.5)
{
if (currentLevel >= height)
{
SkipListEntry<V> p1, p2;
height = height + 1;
p1 = new SkipListEntry<V>(SkipListEntry.negInf, null);
p2 = new SkipListEntry<V>(SkipListEntry.posInf, null);
p1.right = p2;
p1.down = head;
p2.left = p1;
p2.down = tail;
head.up = p1;
tail.up = p2;
head = p1;
tail = p2;
}
while (p.up == null)
{
p = p.left;
}
p = p.up;
SkipListEntry<V> e;
e = new SkipListEntry<V>(k, null);
e.left = p;
e.right = p.right;
e.down = q;
p.right.left = e;
p.right = e;
q.up = e;
q = e;
currentLevel = currentLevel + 1;
}
size = size + 1;
System.out.println("-----插入[" + k + "]之后的跳跃表是:-----");
printHorizontal();
return null;
}
/**
* 根据键删除键值对
* @param key
* @return
* @author xxx 2017年2月14日 下午10:08:17
* @since v1.0
*/
public void remove(String key)
{
SkipListEntry<V> p = findEntry(key);
if(!p.getKey().equals(key)) {
return;
}
p.left.right = p.right;
p.right.left = p.left;
p.right = null;
p.left = null;
while(p.up != null) {
p = p.up;
p.left.right = p.right;
p.right.left = p.left;
p.right = null;
p.left = null;
}
while(p.down != null) {
SkipListEntry<V> temp = p.down;
p.down = null;
temp.up = null;
p = temp;
}
while(head.right.key == tail.key && height > 0) {
SkipListEntry<V> p1, p2;
p1 = head.down;
p2 = tail.down;
head.right = null;
head.down = null;
tail.left = null;
tail.down = null;
p1.up = null;
p2.up = null;
head = p1;
tail = p2;
height = height - 1;
}
size = size - 1;
System.out.println("-----删除[" + key + "]后的跳跃表是:-----");
printHorizontal();
}
/**
* 打印出跳表的图示结构(水平方向)
* @author xxx 2017年2月14日 下午10:35:36
* @since v1.0
*/
public void printHorizontal()
{
String s = "";
int i;
SkipListEntry<V> p;
p = head;
while (p.down != null)
{
p = p.down;
}
i = 0;
while (p != null)
{
p.pos = i++;
p = p.right;
}
p = head;
while (p != null)
{
s = getOneRow(p);
System.out.println(s);
p = p.down;
}
}
private String getOneRow(SkipListEntry<V> p)
{
String s;
int a, b, i;
a = 0;
s = "" + p.key;
p = p.right;
while (p != null)
{
SkipListEntry<V> q;
q = p;
while (q.down != null)
q = q.down;
b = q.pos;
s = s + " <-";
for (i = a + 1; i < b; i++)
s = s + "--------";
s = s + "> " + p.key;
a = b;
p = p.right;
}
return s;
}
/**
* 打印出跳表的图示结构(垂直方向)
* @author xxx 2017年2月14日 下午10:35:36
* @since v1.0
*/
public void printVertical()
{
String s = "";
SkipListEntry<V> p;
p = head;
while (p.down != null)
p = p.down;
while (p != null)
{
s = getOneColumn(p);
System.out.println(s);
p = p.right;
}
}
private String getOneColumn(SkipListEntry<V> p)
{
String s = "";
while (p != null)
{
s = s + " " + p.key;
p = p.up;
}
return (s);
}
}
3 测试
public class Test
{
public static void main(String[] args)
{
SkipList<String> s = new SkipList<String>();
s.put("ABC", "");
s.put("DEF", "");
s.put("KLM", "");
s.put("HIJ", "");
s.put("GHJ", "");
s.put("AAA", "");
s.remove("ABC");
s.remove("DEF");
s.remove("KLM");
s.remove("HIJ");
s.remove("GHJ");
s.remove("AAA");
s.put("ABC", "");
s.put("DEF", "");
s.put("KLM", "");
s.put("HIJ", "");
s.put("GHJ", "");
s.put("AAA", "");
}
}
-----插入[ABC]之前的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之后的跳跃表是:-----
-oo <-> ABC <-> +oo
-oo <-> ABC <-> +oo
-----插入[DEF]之前的跳跃表是:-----
-oo <-> ABC <-> +oo
-oo <-> ABC <-> +oo
-----插入[DEF]之后的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之前的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之后的跳跃表是:-----
-oo <---------> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之前的跳跃表是:-----
-oo <---------> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之后的跳跃表是:-----
-oo <---------> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之前的跳跃表是:-----
-oo <---------> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之后的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之前的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之后的跳跃表是:-----
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-------------------------> GHJ <-----------------> +oo
-oo <-> AAA <-----------------> GHJ <-----------------> +oo
-oo <-> AAA <---------> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[ABC]后的跳跃表是:-----
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-----------------> GHJ <-----------------> +oo
-oo <-> AAA <---------> GHJ <-----------------> +oo
-oo <-> AAA <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> DEF <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[DEF]后的跳跃表是:-----
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <---------> GHJ <-----------------> +oo
-oo <-> AAA <-> GHJ <-----------------> +oo
-oo <-> AAA <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> GHJ <---------> KLM <-> +oo
-oo <-> AAA <-> GHJ <-> HIJ <-> KLM <-> +oo
-----删除[KLM]后的跳跃表是:-----
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <---------> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <---------> +oo
-oo <-> AAA <-> GHJ <-> HIJ <-> +oo
-----删除[HIJ]后的跳跃表是:-----
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <---------> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-oo <-> AAA <-> GHJ <-> +oo
-----删除[GHJ]后的跳跃表是:-----
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-oo <-> AAA <-> +oo
-----删除[AAA]后的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之前的跳跃表是:-----
-oo <-> +oo
-----插入[ABC]之后的跳跃表是:-----
-oo <-> ABC <-> +oo
-----插入[DEF]之前的跳跃表是:-----
-oo <-> ABC <-> +oo
-----插入[DEF]之后的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之前的跳跃表是:-----
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <---------> DEF <-> +oo
-oo <-> ABC <-> DEF <-> +oo
-----插入[KLM]之后的跳跃表是:-----
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之前的跳跃表是:-----
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <---------> DEF <---------> +oo
-oo <-> ABC <-> DEF <-> KLM <-> +oo
-----插入[HIJ]之后的跳跃表是:-----
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之前的跳跃表是:-----
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-----------------> +oo
-oo <---------> DEF <-> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> HIJ <-> KLM <-> +oo
-----插入[GHJ]之后的跳跃表是:-----
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <---------> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之前的跳跃表是:-----
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <-------------------------> +oo
-oo <---------> DEF <---------> HIJ <---------> +oo
-oo <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo
-----插入[AAA]之后的跳跃表是:-----
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <-------------------------> +oo
-oo <-----------------> DEF <---------> HIJ <---------> +oo
-oo <-> AAA <-> ABC <-> DEF <-> GHJ <-> HIJ <-> KLM <-> +oo