概述
常用的數據結構有線性的數組和非線性結構的鏈表,而非線性數據結構中典型的有平衡二叉樹(AVL),紅黑樹,B樹,LSM樹,skiplist 樹,bitmap,hash表等,其中我們經常在各類程序語言中見到的集合容器往往是基於紅黑樹,skiplist,hash表實現的,而數據庫級常用的數據結構往往基於B樹(包括B+,B*)樹,LSM樹等重量級的數據結構。
這裏詳細介紹一下跳錶這個特殊的數據結構,它天然有序的高效的鬆散型數據結構,插入和查找性能與紅黑樹不相上下,而且維護更加簡單粗暴。
跳錶的特點
1.是分層結構,每一層都是有序,雙向連接的
2.每個節點都是一個固定的概率往上跳出
3.第0層是最完整的數據
太懶,不想畫圖,圖片示意來源網上
關鍵邏輯
- 隨機函數。
private int randomLevel() {
int x = randomSeed;
x ^= x << 13;
x ^= x >>> 17;
randomSeed = x ^= x << 5;
if ((x & 0x8001) != 0) // test highest and lowest bits
return 0;
int level = 1;
while (((x >>>= 1) & 1) != 0) ++level;
return level;
}
算法源自jdk 的ConcurrentSkipListMap,比起math.random 更直接,也可直接使用Random
用來返回用來獲取向上跳出的層數
2. 定位
複雜度是常數級別
過程如圖:
代碼示例如下:
/**
* 在最下面一層,找到要插入的位置前面的那個key
* */
private SkipListNode<T> findNode(int key){
SkipListNode<T> p=head;
while(true){
while (p.right.key!=SkipListNode.TAIL_KEY&&p.right.key<=key) {
p=p.right;
}
if (p.down!=null) {
p=p.down;
}else {
break;
}
}
return p;
}
/**
* 查找是否存儲key,存在則返回該節點,否則返回null
* */
public SkipListNode<T> search(int key){
SkipListNode<T> p=findNode(key);
if (key==p.getKey()) {
return p;
}else {
return null;
}
}
- 插入和刪除可以參考下面的邏輯
public Integer insert(int key, int value) {
SkipListEntry p, q;
int i = 0;
// 查找適合插入的位子
p = findEntry(key);
// 如果跳躍表中存在含有key值的節點,則進行value的修改操作即可完成
if(p.key ==key) {
Integer oldValue = p.value;
p.value = value;
return oldValue;
}
// 如果跳躍表中不存在含有key值的節點,則進行新增操作
q = new SkipListEntry(key, value);
/* --------------------------------------------------------------
Insert q into the lowest level after SkipListEntry p:
p put q here p q
| | | |
V V V V V
Lower level: [ ] <------> [ ] ==> [ ] <--> [ ] <--> [ ]
--------------------------------------------------------------- */
q.left = p;
q.right = p.right;
p.right.left = q;
p.right = q;
//本層操作完畢,看更高層操作
//拋硬幣隨機決定是否上層插入
while ( r.nextDouble() < 0.5 /* Coin toss */ )
{
if ( i >= h ) // We reached the top level !!!
{
//Create a new empty TOP layer
addEmptyLevel();
}
/* ------------------------------------
Find first element with an UP-link
------------------------------------ */
while ( p.up == null )
{
p = p.left;
}
/* --------------------------------
Make p point to this UP element
-------------------------------- */
p = p.up;
/* ---------------------------------------------------
Add one more (k,*) to the column
Schema for making the linkage:
p <--> e(k,*) <--> p.right
^
|
v
q
---------------------------------------------------- */
SkipListEntry e;
// 這裏需要注意的是除底層節點之外的節點對象是不需要value值的
e = new SkipListEntry(key, null);
/* ---------------------------------------
Initialize links of e
--------------------------------------- */
e.left = p;
e.right = p.right;
e.down = q;
/* ---------------------------------------
Change the neighboring links..
--------------------------------------- */
p.right.left = e;
p.right = e;
q.up = e;
//把q執行新插入的節點:
q = e;
// level增加
i = i + 1;
}
n = n+1; //更新鏈表長度
return null;
}
private void addEmptyLevel() {
SkipListEntry p1, p2;
p1 = new SkipListEntry(Integer.MIN_VALUE, null);
p2 = new SkipListEntry(Integer.MAX_VALUE, null);
p1.right = p2;
p1.down = head;
p2.left = p1;
p2.down = tail;
head.up = p1;
tail.up = p2;
head = p1;
tail = p2;
h = h + 1;
}
public Integer remove(int key) {
SkipListEntry p, q;
p = findEntry(key);
if(!p.key.equals(key)) {
return null;
}
Integer oldValue = p.value;
while(p != null) {
q = p.up;
p.left.right = p.right;
p.right.left = p.left;
p = q;
}
return oldValue;
}
參考原文鏈接:https://blog.csdn.net/bohu83/article/details/83654524