SkipTable的简单实现
skiptable是一种高效的数据结构,增删改查的速度和红黑树不相上下,有着广泛的用途,例如大名鼎鼎的redis就是使用了skiptable作为核心的数据结构。
skiptable的思想主要是随机化和二分法。
首先介绍skiptable的模型长什么样子。
它是链表的升级版。
链表的查找速度为O(n)
,而skiptable的查找速度为O(lg n)
。
skiptable主要在链表上做了以下改动:
为每一个元素生成随机的n层索引。
level 4: 5
level 3: 3 5
level 2: 2 3 5
level 1: 1 2 3 4 5
如上,1、2、3、4、5分别生成了1、2、3、1、4层的索引
在查找的时候,遵循以下步骤:
- 从最高层开始寻找
- 找到该层大于要寻找的key的第一个元素的前一个元素
- 向下移到下一层
- 每一层最左边为无穷小,最右边为无穷大
例如,要寻找4,路径为:
level4, - infty->
level3, 3->
level2, 3->
level1, 3->
level1, 4
最坏的查找次数为N * 2 + L
,其中N是元素在链表level1中的位值,L是最大层数
最好的查找次数为L
,即最大的层数
平均查找次数为lg N
现在使用十字交叉单链表进行跳表的简单实现。
其中,将字符串作为key值,使用hash函数将key映射为一个唯一的整数,然后进行存取。
数据结构
typedef int data_t;
typedef char* rawK_t;
typedef size_t k_t;
typedef struct
{
k_t key;
data_t data;
}node_t;
typedef struct st_t
{
union
{
int level;
node_t node;
}body;
struct st_t* next, *down;
}st_t, *st_pt;
#define KEY body.node.key
#define DATA body.node.data
#define LEVEL body.level
typedef struct
{
int success;
data_t data;
}res_t;
具体函数说明
static k_t inline hash(rawK_t rawKey)
{
k_t res = 0;
int len = 0;
while (len < MAX_RAWKEY_LEN &&
!isspace(rawKey[len]))
{
res += res * 131 + (k_t)rawKey[len ++];
}
return res;
}
参考自:https://www.cnblogs.com/zl1991/p/11820922.html
将字符串映射为一个唯一的整数值
static void init()
{
root = (st_pt *)malloc(MAX_LEVEL * sizeof(st_pt));
int i;
st_pt tmp = NULL;
for (i = 0; i < MAX_LEVEL; i ++)
{
root[i] = (st_pt)malloc(sizeof(st_t));
root[i]->next = NULL;
root[i]->LEVEL = i;
root[i]->down = tmp;
tmp = root[i];
}
srand(time(NULL));
}
初始化跳表结构
static st_pt __search(k_t key)
{
st_pt cur = root[MAX_LEVEL - 1];
st_pt prev;
while (cur != NULL)
{
while (cur->next != NULL && cur->next->KEY <= key)
cur = cur->next;
prev = cur;
cur = cur->down;
}
return prev;
}
返回查找到的最大的不大于key值的节点
static int inline randLevel()
{
int level = 0;
while (rand() % 2)
level += 1;
return level;
}
随机化索引层数
具体操作
增
首先判断key值是否已经被记录,若未记录,则随机化索引层数,然后插入
res_t insert(rawK_t rawKey, data_t data) // 增
{
if (!root)
init();
k_t key = hash(rawKey);
st_pt prev = __search(key);
if (prev->KEY == key)
{
res_t res = {
false};
printf("%s already exists\n", rawKey);
return res;
}
// 插入
int maxLevel = randLevel();
st_pt tmp = <