跳跃表
跳跃表是redis底层数据结构之一,具有较好的性能;本文主要整理了跳跃表的的基本原理以及JAVA实现的具体流程.
个人主页:tuzhenyu’s page
原文地址:跳跃表
(0) 概念
跳跃表是一组带有指针数组指向后续节点的有序链表;
跳跃表和红黑树,AVL树等类似都是一种基于排序的索引结构,效率和红黑树差不多但是实现原理比红黑树简单;
跳跃表的另一个特点就是相比于红黑树具有更好的并发性能,红黑树的插入删除需要对整棵树加锁进行结构调整影响了其并发性能;
(1) 跳跃表的实现
1.表节点类的定义
- 跳跃表节点主要包含一个指针数组和节点值,节点指针数组默认为32,也就是说节点指针最多有32层;节点指针最稠密的情况下32层指针可以容纳至少2的32次方个节点;
class SkipNode<E extends Comparable<? super E>>{
public E val;
public SkipNode<E>[] next;
public SkipNode(int max_level,E val){
this.val = val;
next = new SkipNode[max_level];
}
}
2.跳跃表的初始化
根据定义的概率参数P计算得出对应的最大层max_level
初始化跳跃表,创建头节点header和尾节点NIL,并将头结点的所有层指针都指向尾节点;
头节点的指针数组高度为定义的level最大值max_level
public SkipLIst(){
this(OPTIMAL_P,(int)Math.ceil(Math.log(Integer.MAX_VALUE) / Math.log(1 / OPTIMAL_P)) - 1);
}
public SkipLIst(double probability, int maxLevel){
P = probability;
MAX_LEVEL = maxLevel;
header = new SkipNode<E>(MAX_LEVEL,null);
NIL = new SkipNode<E>(MAX_LEVEL,null);
for (int i=0;i<header.next.length;i++){
header.next[i] = NIL;
}
}
3.查找
- 从当前跳跃表的最大层开始向下查找特定节点,遵循从左上角搜索到右下角的原则,
public boolean contain(E val){
SkipNode<E> cur = header;
for (int i=level;i>=0;i--){
while (cur.next[i]!=null&&cur.next[i].val.compareTo(val)<0){
cur = cur.next[i];
}
if (cur.next[i]!=null&&cur.next[i].val==val)
return true;
}
return false;
}
4.插入
- 随机获取插入节点的level高度,根据威廉姆的论文中最佳的概率参数为0.25.并根据概率参数计算出最大level为15;
private int randomLevel(){
int nextLevel = 1;
while (nextLevel<MAX_LEVEL&&Math.random()<P){
nextLevel++;
}
return nextLevel;
}
根据计算随机得出的level,创建新节点并插入到原来的跳跃表中;
从当前跳跃表最高层开始向下寻找每一层小于插入值的边界节点
判断边界节点的值是否和要插入的值相等,如果相等则更新否则则插入新的节点;
如果随机产生的level值大于当前跳跃表的level则将大于的部分指向NIL;将小于的部分指向边界节点的下一节点,插入到边界节点与下一节点之间;
如果随机产生的level值小于当前跳跃表的level则直接将小于的部分指向边界节点的下一节点,插入到边界节点与下一节点之间;
public void insert(E val){
SkipNode<E> cur = header;
SkipNode<E>[] processors = new SkipNode[MAX_LEVEL];
for (int i=level;i>=0;i--){
while (cur.next[i]!=null&&cur.next[i].val.compareTo(val)<0){
cur = cur.next[i];
}
processors[i] = cur;
}
cur = cur.next[0];
if (cur.val.equals(val)){
cur.val = val;
}else {
int nextLevel = randomLevel();
if (nextLevel>level){
for (int i=level;i<nextLevel;i++){
processors[i] = header;
}
level = nextLevel;
}
SkipNode<E> node = new SkipNode<E>(nextLevel,val);
for (int j=0;j<level;j++){
node.next[j] = processors[j].next[j];
processors[j].next[j] = node;
}
}
}
5.删除
删除指定值的节点
从当前跳跃表最高层开始向下寻找每一层小于插入值的边界节点
将指向删除节点的指针指向下一节点
删除节点后刷新level的高度;
public void delete(E val){
SkipNode<E> cur = header;
SkipNode<E>[] processors = new SkipNode[MAX_LEVEL];
for (int i=level-1;i>=0;i--){
while (cur.next[i]!=null&&cur.next[i].val.compareTo(val)<0){
cur = cur.next[i];
}
processors[i] = cur;
}
cur = cur.next[0];
if (cur.val==val){
for (int i=0;i<level;i++){
if (processors[i].next[i]!=val){
break;
}
processors[i].next[i] = cur.next[i];
}
while (level>0&&header.next[level-1]==NIL)
level--;
}
}