原理参考:SkipList跳表
这里我使用Java实现其原理:
首先是SkipListNode的定义:
SkipListNode.java
package skiplist; /** * Created by zhuxinquan on 17-3-11. */ public class SkipListNode implements Comparable { private int value; private SkipListNode next = null; private SkipListNode downNext = null; @Override protected void finalize() throws Throwable { super.finalize(); System.out.printf("123"); } public int getValue() { return value; } public void setValue(int value) { this.value = value; } public SkipListNode getNext() { return next; } public void setNext(SkipListNode next) { this.next = next; } public SkipListNode getDownNext() { return downNext; } public void setDownNext(SkipListNode downNext) { this.downNext = downNext; } @Override public int compareTo(Object o) { return this.value > ((SkipListNode)o).value ? 1 : -1; } }然后是跳跃表的具体操作,操作过程在开始部分处的链接已明了,这里直接模拟实现:
SkipList.java
package skiplist; import java.util.Random; /** * Created by zhuxinquan on 17-3-11. */ public class SkipList { public int level = 0; public SkipListNode top = null; public SkipList() { this(4); } //跳跃表的初始化 public SkipList(int level) { this.level = level; SkipListNode skipListNode = null; SkipListNode temp = top; SkipListNode tempDown = null; SkipListNode tempNextDown = null; int tempLevel = level; while(tempLevel -- != 0){ skipListNode = createNode(Integer.MIN_VALUE); temp = skipListNode; skipListNode = createNode(Integer.MAX_VALUE); temp.setNext(skipListNode); temp.setDownNext(tempDown); temp.getNext().setDownNext(tempNextDown); tempDown = temp; tempNextDown = temp.getNext(); } top = temp; } //随机产生数k,k层下的都需要将值插入 public int randomLevel(){ int k = 1; while(new Random().nextInt()%2 == 0){ k ++; } return k > level ? level : k; } //查找 public SkipListNode find(int value){ SkipListNode node = top; while(true){ while(node.getNext().getValue() < value){ node = node.getNext(); } if(node.getDownNext() == null){ //返回要查找的节点的前一个节点 return node; } node = node.getDownNext(); } } //删除一个节点 public boolean delete(int value){ int tempLevel = level; SkipListNode skipListNode = top; SkipListNode temp = skipListNode; boolean flag = false; while(tempLevel -- != 0){ while(temp.getNext().getValue() < value){ temp = temp.getNext(); } if(temp.getNext().getValue() == value){ temp.setNext(temp.getNext().getNext()); flag = true; } temp = skipListNode.getDownNext(); } return flag; } //插入一个节点 public void insert(int value){ SkipListNode skipListNode = null; int k = randomLevel(); SkipListNode temp = top; int tempLevel = level; SkipListNode tempNode = null; //当在第n行插入后,在第n - 1行插入时需要将第n行backTempNode的DownNext域指向第n - 1的域 SkipListNode backTempNode = null; int flag = 1; while(tempLevel-- != k){ temp = temp.getDownNext(); } tempLevel++; tempNode = temp; //小于k层的都需要进行插入 while(tempLevel-- != 0){ //在第k层寻找要插入的位置 while(tempNode.getNext().getValue() < value){ tempNode = tempNode.getNext(); } skipListNode = createNode(value); //如果是顶层 if(flag != 1){ backTempNode.setDownNext(skipListNode); } backTempNode = skipListNode; skipListNode.setNext(tempNode.getNext()); tempNode.setNext(skipListNode); flag = 0; tempNode = tempNode.getDownNext(); } } //创建一个节点 private SkipListNode createNode(int value){ SkipListNode node = new SkipListNode(); node.setValue(value); return node; } }然后再定义一个测试类如下:
SkipListTest.java
package skiplist; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; /** * Created by zhuxinquan on 17-3-11. */ public class SkipListTest { public static void main(String[] args) { long startTime = 0; long endTime = 0; SkipList skipList = new SkipList(12); Set<Integer> set = new HashSet(); List<Integer> list = new LinkedList<>(); //测试跳跃表性能 startTime = System.currentTimeMillis(); for (int i = 1; i < 100000; i++) { skipList.insert(i); } endTime = System.currentTimeMillis(); System.out.printf("createSkipList:%d\n", endTime - startTime); startTime = System.currentTimeMillis(); System.out.printf("find(555):%d\n", skipList.find(55555).getNext().getValue()); endTime = System.currentTimeMillis(); System.out.printf("skipListFindTime:%d\n\n", endTime - startTime); //测试LinkedList性能 startTime = System.currentTimeMillis(); for (int i = 1; i < 100000; i++) { list.add(i); } endTime = System.currentTimeMillis(); System.out.printf("createList:%d\n", endTime - startTime); startTime = System.currentTimeMillis(); System.out.printf("find(555):%b\n", list.contains(55555)); endTime = System.currentTimeMillis(); System.out.printf("linkedListFindTime:%d\n\n", endTime - startTime); //测试hashSet性能 startTime = System.currentTimeMillis(); for (int i = 1; i < 100000; i++) { set.add(i); } endTime = System.currentTimeMillis(); System.out.printf("createSet:%d\n", endTime - startTime); startTime = System.currentTimeMillis(); System.out.printf("find(555):%b\n", set.contains(55555)); endTime = System.currentTimeMillis(); System.out.printf("hashSetFindTime:%d\n", endTime - startTime); } }上述测试类对100000条数据进行插入和查找,分别统计使用跳跃表、LinkedList、HashSet时插入和查找的时间,结果如下:
下面是50000整数中查找38555这个值消耗的时间:
上图很明显的可以看出,跳跃表的查找效率还是很高的(在数据量高的时候),同时,跳跃表也肯定消耗了不少的额外空间。
上述代码只是个人根据自己的理解简单实现了跳跃表的原理,其中自然有不少瑕疵,也有好多可优化的地方,比如可以将每层的起点节点的引用保存在一个一维数组当中、跳表中存储键值对这样的数据结构等等。。。。
个人理解,如有不足之处,还望指正!