最近在看各种平衡树。
跳表也是平衡树的一种替代方案,优点是实现较为简单。它以多于原始数据一倍的存储空间换取查询效率。
发明的动机应该是想对链表进行二分查找,逐渐演化出的算法。
跳表在工程领域有广泛的应用。 比如 redis的zset,rocksdb中的内存部分实现等。也是一些语言基础包中的数据结构。
跳表,下面这篇文章写的真的很不错
https://www.jianshu.com/p/9d8296562806
于是读完之后仿照思路实现了一遍,可以拿 leetcode 1206 来验证正确性.
实现过程中体会到下面几点
- 跳表采用空间换时间的方式,比原始数据存储量增加一倍左右,概率上每一层是上一层的数据量的一半。原题目数据量 <= 50000, 本题目按照一般情况设置为16层。
- 不管是插入还是删除一个元素,最关键是找到么一层中符合条件插入位置或者删除元素的上一个位置。是一条分界线, 核心下面的代码中searchHit(int num)函数。
- 在吗每一层头部添加哨兵,尾部共同聚合到一个值为无穷大的节点,可以简化代码的编写
- 跳表的节点采用预分配的形式,并采用垃圾回收机制.
leetcode 1206 的代码
class Skiplist {
public:
// 预分配和垃圾回收机制
const static int MAX_LEVEL = 16, INF = 30010, N = 50010;
float ratio = 0.5;
struct Node {
int down = 0, next = 0, val , maxLevel = 0;
void init(int _val, int _maxLevel, int _down, int _next){
down =_down, val = _val, maxLevel =_maxLevel, next = _next;
}
};
int idx = 0, head, nodes[N], tt = -1;
Node tr[N];
Skiplist() {
// 初始化尾部哨兵节点
tr[0].init(INF, 0, 0, 0);
int u = head = getNode();
tr[u].init(-INF, MAX_LEVEL, 0, 0);
// 为每一层添加头部哨兵节点
for (int i = MAX_LEVEL - 1; i > 0; i--) {
int v = getNode();
tr[u].down = v;
tr[v].init(-INF, i, 0, 0);
u = v;
}
}
int getNode() {
if (tt < 0) nodes[++tt] = ++ idx;
int u = nodes[tt--];
return u;
}
void gcNode(int u) {
nodes[++tt] = u;
}
// 核心函数
int update[MAX_LEVEL + 1];
int searchHit(int num) {
int u = head, hit = 0, p;
for (int level = MAX_LEVEL; level > 0; level --) {
p = u;
while (tr[p].val < num) u = p, p = tr[p].next;
if (tr[p].val == num) hit = max(level, hit);
update[level] = u;
u = tr[u].down;
}
return hit;
}
bool search(int target) {
return searchHit(target);
}
void add(int num) {
searchHit(num);
int last = 0, randLevel = randomLevel(), t, c, u;
for (int level = randLevel; level > 0; level --) {
u = getNode();
Node & node = tr[u];
node.init(num, level, 0, 0);
c = update[level];
t = tr[c].next;
tr[c].next = u, node.next = t;
if (last) tr[last].down = u;
last = u;
}
}
bool erase(int num) {
int hit = searchHit(num);
if (!hit) return false;
int p, t;
for(int level = hit; level > 0; level --) {
p = update[level];
t = tr[p].next;// t 一定存在
tr[p].next = tr[t].next;
gcNode(t);
}
return true;
}
/**
* P(level=1)=0.5 P(level=2)=0.25 ...
*/
int randomLevel() {
int level = 0;
while (rand() % 100 < ratio * 100 && level < MAX_LEVEL) {
level ++;
}
return level + 1;
}
};