跳表——SkipTable的简单实现

跳表(SkipTable)是一种高效的数据结构,其查找速度接近红黑树,被广泛应用于如Redis等系统。本文介绍了跳表的模型、查找原理,并通过十字交叉单链表实现了一个简单的跳表,包括增、删、改、查的操作。同时,提供了具体的函数说明和测试结果。
摘要由CSDN通过智能技术生成

SkipTable的简单实现

skiptable是一种高效的数据结构,增删改查的速度和红黑树不相上下,有着广泛的用途,例如大名鼎鼎的redis就是使用了skiptable作为核心的数据结构。

skiptable的思想主要是随机化和二分法。

首先介绍skiptable的模型长什么样子。

它是链表的升级版。
链表的查找速度为O(n),而skiptable的查找速度为O(lg n)
skiptable主要在链表上做了以下改动:
为每一个元素生成随机的n层索引。

level 45
level 33   5
level 22 3   5
level 11 2 3 4 5

如上,1、2、3、4、5分别生成了1、2、3、1、4层的索引

在查找的时候,遵循以下步骤:

  1. 从最高层开始寻找
  2. 找到该层大于要寻找的key的第一个元素的前一个元素
  3. 向下移到下一层
  4. 每一层最左边为无穷小,最右边为无穷大

例如,要寻找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 = <
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redis中的跳表Skip List)是一种有序数据结构,用于实现有序集合(Sorted Set)的数据存储和查询。跳表通过添加多级索引来加速查询操作。 跳表实现主要包含以下几个步骤: 1. 跳表节点的定义:定义一个节点结构,包含键值对以及多个指向下一个节点的指针,其中指针的数量由一个随机函数决定,通常设置为1到32之间。 2. 跳表层级的定义:定义一个层级结构,包含多个指向不同层级的节点的指针。每个层级都是一个链表,其中最底层是原始数据链表,每个节点按照键值进行排序。 3. 插入操作:在插入新节点时,需要选择节点要插入的层级。从最高层级开始,逐层向下遍历,直到找到插入位置。在遍历过程中,如果遇到相同键值的节点,则更新节点的值;如果没有找到相同键值的节点,则将新节点插入到对应位置,并将相关指针进行更新。 4. 删除操作:在删除节点时,需要找到对应键值的节点,并将相关指针进行更新。如果删除后某个层级中没有节点了,则需要将该层级删除。 5. 查询操作:在查询某个键值对应的节点时,从最高层级开始,逐层向下遍历,直到找到节点或者遍历到最底层。在遍历过程中,根据节点的键值与目标键值的大小关系,决定向右还是向下移动。 跳表的优点是查询效率高,时间复杂度为O(log N),与平衡二叉树相当。同时,跳表实现相对简单,不需要进行平衡操作,适用于实际应用中有序集合的存储和查询需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值