对于顺序单链表来说,插入的时间复杂度是O(1),但是查找的时间复杂度是O(n),因为没法进行二分查找。
但是如果链表每几个结点建立一个索引,那么就类似于二分查找了。跳表就是这种思想。
对于怎么建立索引,一般来说,都是每几个结点抽出来一个结点,然后作为索引结点。跳表思想类似,不过采用的是一种随机化的思想。比如想要每两个结点抽出来一个
作为索引结点。那么每一个结点被抽取的概率是二分之一,然后建立一个新的链表。然后对于新的链表重复以上的过程。直到链表个数到了给定的值为止。
所以说,是这样的一个链表:
查找:
插入:
删除:
实现细节:
class Skiplist {
int level = 10;
class Node {
int val;
Node[] ne = new Node[level];
Node(int _val) {
val = _val;
}
}
Random random = new Random();
Node he = new Node(-1);
private void find(int t, Node[] ns) { // 找到待插入结点的前一个结点
Node cur = he;
for (int i = level - 1; i >= 0; i--) {
while (cur.ne[i] != null && cur.ne[i].val < t) cur = cur.ne[i];
ns[i] = cur;
}
}
public boolean search(int t) {
Node[] ns = new Node[level];
find(t, ns);
return ns[0].ne[0] != null && ns[0].ne[0].val == t;
}
public void add(int t) {
Node[] ns = new Node[level];
find(t, ns);
Node node = new Node(t);
for (int i = 0; i < level; i++) {
node.ne[i] = ns[i].ne[i];
ns[i].ne[i] = node;
if (random.nextInt(2) == 0) break;
}
}
public boolean erase(int t) {
Node[] ns = new Node[level];
find(t, ns);
Node node = ns[0].ne[0];
if (node == null || node.val != t) return false;
for (int i = 0; i < level && ns[i].ne[i] == node; i++) ns[i].ne[i] = ns[i].ne[i].ne[i];
return true;
}
}