跳表
跳表是一种随机化数据结构,其功能和性能可媲美平衡树,但实现却要简单很多,在redis和LevelDB中有大量使用。下面以跳表实现集合为例进行说明,要求如下:
- 为简单起见,元素均为整型值;
- 支持元素的插入、删除、查找等;
- 各种操作时间复杂度不超过O(logn);
结构定义
跳表是在链表的基础上每个节点增加了多级指针形成的,先定义节点:
typedef struct Node {
int key;
int height;
struct Node *next[0];
}Node;
这里用到了变长数组,它需要C99标准的支持。有了节点定义后可定义跳表:
typedef struct SkipList {
int size;
int height;
Node *head;
}SkipList;
创建跳表
一般来讲,应根据数据量大小定义合理的跳表最大层数,以节省空间,并且不影响性能。创建跳表要做的主要工作就是初始化满层的表头。
Node* CreateNode(int key, int height) {
int i;
Node *p = malloc(sizeof(Node) + sizeof(Node*) * (1 + height));
p->key = key;
p->height= height;
for (i = 0; i <= height; i++)
p->next[i] = NULL;
return p;
}
SkipList* CreateList(void) {
srand(time(NULL)); /* prepare for rand() */
SkipList *sl = malloc(sizeof(SkipList));
sl->size = sl->height = 0;
sl->head = CreateNode(INT_MIN, MAX_HEIGHT);
return sl;
}
插入元素
跳表的插入与链表类似,区别在于节点是有高度的,为了近可能得达到平衡,理想的高度随机性应满足几何分布,可用类似投硬币的方法来模拟,这也是跳表随机化的关键。
int PickHeight(void) {
int h = 0;
while (rand() & 1)
h += 1;
return h <= MAX_HEIGHT ? h : MAX_HEIGHT;
}
在确定待插入元素的高度后,接下来便要找插入位置,由于插入时可能会影响到前面节点的next指针,可在找插入位置时顺带把需要变更的节点记录下来。
Node* Insert(SkipList *sl, int key) {
Node *stack[MAX_HEIGHT+1], *p = sl->head;
int i, j, h = sl->height, idx = 0;
while (h >= 0) {
if (NULL == p->next[h] || p->next[h]->key > key) {
stack[idx++] = p;
h -= 1;
} else if (p->next[h]->key < key) {
p = p->next[h];
} else { /* already exist */
return p->next[h];
}
}
h = PickHeight();
p = CreateNode(key, h);
for (i = 0, j = idx - 1; i <= h && j >= 0; i++, j--) {
p->next[i] = stack[j]->next[i];
stack[j]->next[i] = p;
}
while (sl->height < h) {
sl->height += 1;
sl->head->next[sl->height] = p;
}
sl->size += 1;
return p;
}
删除元素
删除与插入类似,在找待删除元素过程中记录下可能需要变更next指针的节点,区别在于:插入时可能会多记高层节点,因插入节点的高度不够而导致无需修改;而删除只记next指针指向待删除节点的节点,所有这些节点的next指针都必须修改。
int Delete(SkipList *sl, int key) {
Node *stack[MAX_HEIGHT+1], *p = sl->head;
int i, j, idx = 0, h = sl->height;
while (h >= 0) {
if (NULL == p->next[h] || p->next[h]->key > key) {
h -= 1;
} else if (p->next[h]->key < key) {
p = p->next[h];
} else {
stack[idx++] = p;
h -= 1;
}
}
if (0 == idx) return 0; /* not exist */
p = p->next[0]; /* now p is the node to be deleted */
for (i = 0, j = idx - 1; j >= 0; i++, j--) {
stack[j]->next[i] = p->next[i];
if (INT_MIN == stack[j]->key && NULL == p->next[i]) /* no elements in this level */
sl->height -= 1;
}
free(p);
sl->size -= 1;
return 1;
}
查找元素
查找比插入和删除容易多了,从表头顶开始,优先往右、不行就往下走。
Node* Find(SkipList *sl, int key) {
int h = sl->height;
Node *p = sl->head;
while (h >= 0) {
if (p->next[h] && p->next[h]->key <= key)
p = p->next[h];
else h -= 1;
}
return p->key == key ? p : NULL;
}
跳表大小
int GetListSize(const SkipList *sl) {
return sl->size;
}
释放跳表
void FreeList(SkipList *sl) {
Node *p = sl->head, *q;
while (p) {
q = p;
p = p->next[0];
free(q);
}
free(sl);
}