Skip Lists: A Probabilistic Alternative to Balanced Trees

本文介绍了跳表这一高效数据结构,对比平衡树和伸展树,跳表在空间占用及实现难度上有明显优势。文中提到动态跳表通过随机确定层级的方式在概率上保持高效性,并探讨了不同概率设置下对查找效率的影响。

原文:http://people.csail.mit.edu/shanir/publications/OPODIS2006-BA.pdf

最近不断听说常用跳表实现内存索引(leveldb/redis/hbase), 研究了这篇1990的论文,跳表一种比平衡树和伸展树空间更节约、更易于实现且非常高效的数据结构。

原来认识到的只是静态跳表,动态跳表则是每次随机确定一个level,概率上非常高效。


作者建议p选1/4,因为这是平均查找次数和1/2是一样的,但需要的指针只要1.33。当然,如果对于查找次数的方差很在意,还是使用1/2。

跳表是一种随机化的数据结构,可在 $O(\log n)$ 平均时间复杂度下完成插入、删除和查找操作。以下是一个简单的 C 语言实现跳表查找功能的示例代码,它基于你提供的部分代码并进行了完善: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> // 定义跳表节点结构体 typedef struct Node { int key; int level; struct Node **forward; } Node; // 定义跳表结构体 typedef struct Skiplist { int maxLevel; Node *header; } Skiplist; // 创建新节点 Node* createNode(int key, int level) { Node *node = (Node*)malloc(sizeof(Node)); node->key = key; node->level = level; node->forward = (Node**)malloc((level + 1) * sizeof(Node*)); for (int i = 0; i <= level; i++) { node->forward[i] = NULL; } return node; } // 创建新跳表 Skiplist* getNewSkiplist(int maxLevel) { Skiplist *s = (Skiplist*)malloc(sizeof(Skiplist)); s->maxLevel = maxLevel; s->header = createNode(-1, maxLevel); return s; } // 随机生成节点的层级 int randomLevel(int maxLevel) { int level = 0; while ((rand() % 2) && level < maxLevel) { level++; } return level; } // 插入元素到跳表 void insert(Skiplist *s, int key) { Node *update[s->maxLevel + 1]; Node *p = s->header; for (int i = s->maxLevel; i >= 0; i--) { while (p->forward[i] != NULL && p->forward[i]->key < key) { p = p->forward[i]; } update[i] = p; } p = p->forward[0]; if (p == NULL || p->key != key) { int newLevel = randomLevel(s->maxLevel); if (newLevel > s->maxLevel) { for (int i = s->maxLevel + 1; i <= newLevel; i++) { update[i] = s->header; } s->maxLevel = newLevel; } Node *newNode = createNode(key, newLevel); for (int i = 0; i <= newLevel; i++) { newNode->forward[i] = update[i]->forward[i]; update[i]->forward[i] = newNode; } } } // 查找元素 Node* find(Skiplist *s, int key) { Node *p = s->header; for (int i = s->maxLevel; i >= 0; i--) { while (p->forward[i] != NULL && p->forward[i]->key < key) { p = p->forward[i]; } } p = p->forward[0]; if (p != NULL && p->key == key) { return p; } return NULL; } // 清理跳表 void cleanSkiplist(Skiplist *s) { Node *p = s->header; while (p != NULL) { Node *temp = p; p = p->forward[0]; free(temp->forward); free(temp); } free(s); } // 打印跳表结构(简单示例) void output(Skiplist *s) { Node *p = s->header->forward[0]; while (p != NULL) { printf("%d ", p->key); p = p->forward[0]; } printf("\n"); } int main() { srand(time(0)); Skiplist *s = getNewSkiplist(32); // 创建最大32层的跳表 int x; // 插入测试 while (~scanf("%d", &x)) { if (x == -1) break; insert(s, x); output(s); // 打印当前结构 } // 查找测试 while (~scanf("%d", &x)) { Node *p = find(s, x); if (p) printf("找到值%d,位于%d层\n", p->key, p->level); else printf("未找到%d\n", x); } cleanSkiplist(s); return 0; } ``` ### 代码解释: 1. **节点和跳表结构体**:定义了 `Node` 结构体表示跳表的节点,包含键值、层级和指向前驱节点的指针数组;`Skiplist` 结构体表示跳表,包含最大层级和头节点。 2. **创建节点和跳表**:`createNode` 函数用于创建新节点,`getNewSkiplist` 函数用于创建新的跳表。 3. **随机层级生成**:`randomLevel` 函数随机生成节点的层级,以保证跳表的随机性。 4. **插入操作**:`insert` 函数将元素插入到跳表中,通过更新 `update` 数组来找到合适的插入位置。 5. **查找操作**:`find` 函数用于在跳表中查找指定的键值,通过逐层遍历找到目标节点。 6. **清理操作**:`cleanSkiplist` 函数用于释放跳表占用的内存。 ### 查找流程: 1. 从跳表的最高层开始,逐层向下遍历,找到第一个大于等于目标键值的节点。 2. 如果找到的节点的键值等于目标键值,则返回该节点;否则返回 `NULL`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值