跳表的学习

跳表是一种随机化的数据结构,用于快速查找、插入和删除操作,其性能与红黑树、AVL树相当。由William Pugh发明,跳表通过在有序链表上建立多级索引来加速搜索,平均时间复杂度为O(logn)。文章介绍了跳表的搜索、添加和删除操作的算法,并提供了一个简单的C语言实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。

跳表这种数据结构是由William Pugh 发明的,关于跳表的详细介绍可以参考论文:「Skip Lists: A Probabilistic Alternative to Balanced Trees」,论文中详细阐述了关于 skiplist 查找元素、删除元素、插入元素的算法伪代码,以及时间复杂度的分析。

跳表是一种随机化的数据结构,可以被看做二叉树的一个变种,它在性能上和红黑树、AVL 树不相上下,但是跳表的原理非常简单,目前在 Redis 和LevelDB 中都有用到。跳表的期望空间复杂度为O(n),跳表的查询,插入和删除操作的期望时间复杂度均为 O(logn)。

search:从跳表的当前的最大层数 level 层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第 1 层。此时,若第 1 层的下一个节点的值等于 target,则返回true;反之,则返回 false。

add:从跳表的当前的最大层数level 层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第 1 层。设新加入的节点为 newNode,我们需要计算出此次节点插入的层数 lv,如果 level 小于 lv,则同时需要更新level。我们用数组 update 保存每一层查找的最后一个节点,第 i 层最后的节点为 update[i]。我们将newNode 的后续节点指向 update[i] 的下一个节点,同时更新 update[i] 的后续节点为 newNode。

erase:首先我们需要查找当前元素是否存在跳表中。从跳表的当前的最大层数 level 层开始查找,在当前层水平地逐个比较直至当前节点的下一个节点大于等于目标节点,然后移动至下一层进行查找,重复这个过程直至到达第 1层。如果第 1层的下一个节点不等于 num 时,则表示当前元素不存在直接返回。我们用数组update 保存每一层查找的最后一个节点,第 i层最后的节点为 update[i]。此时第 i层的下一个节点的值为num,则我们需要将其从跳表中将其删除。由于第 i层的以 pp 的概率出现在第 i+1 层,因此我们应当从第 1 层开始往上进行更新,将num 从update[i] 的下一跳中删除,同时更新 update[i] 的后续节点,直到当前层的链表中没有出现 num 的节点为止。最后我们还需要更新跳表中当前的最大层数 level。

#define MAX(a, b) ((a) > (b) ? (a) : (b))
const int MAX_LEVEL = 32;
const int P_FACTOR = RAND_MAX >> 2;

typedef struct SkiplistNode {
    int val;
    int maxLevel;
    struct SkiplistNode **forward;
} SkiplistNode;

typedef struct {
    SkiplistNode *head;
    int level;
} Skiplist;

SkiplistNode *skiplistNodeCreat(int val, int maxLevel) {
    SkiplistNode *obj = (SkiplistNode *)malloc(sizeof(SkiplistNode));
    obj->val = val;
    obj->maxLevel = maxLevel;
    obj->forward = (SkiplistNode **)malloc(sizeof(SkiplistNode *) * maxLevel);
    for (int i = 0; i < maxLevel; i++) {
        obj->forward[i] = NULL;
    }
    return obj;
}

void skiplistNodeFree(SkiplistNode* obj) {
    if (obj->forward) {
        free(obj->forward);
        obj->forward = NULL;
        obj->maxLevel = 0;
    }
    free(obj);
}

Skiplist* skiplistCreate() {
    Skiplist *obj = (Skiplist *)malloc(sizeof(Skiplist));
    obj->head = skiplistNodeCreat(-1, MAX_LEVEL);
    obj->level = 0;
    srand(time(NULL));
    return obj;
}

static inline int randomLevel() {
    int lv = 1;
    /* 随机生成 lv */
    while (rand() < P_FACTOR && lv < MAX_LEVEL) {
        lv++;
    }
    return lv;
}

bool skiplistSearch(Skiplist* obj, int target) {
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 target 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < target) {
            curr = curr->forward[i];
        }
    }
    curr = curr->forward[0];
    /* 检测当前元素的值是否等于 target */
    if (curr && curr->val == target) {
        return true;
    } 
    return false;
}

void skiplistAdd(Skiplist* obj, int num) {
    SkiplistNode *update[MAX_LEVEL];
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 num 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < num) {
            curr = curr->forward[i];
        }
        update[i] = curr;
    }
    int lv = randomLevel();
    if (lv > obj->level) {
        for (int i = obj->level; i < lv; i++) {
            update[i] = obj->head;
        }
        obj->level = lv;
    }
    SkiplistNode *newNode = skiplistNodeCreat(num, lv);
    for (int i = 0; i < lv; i++) {
        /* 对第 i 层的状态进行更新,将当前元素的 forward 指向新的节点 */
        newNode->forward[i] = update[i]->forward[i];
        update[i]->forward[i] = newNode;
    }
}

bool skiplistErase(Skiplist* obj, int num) {
    SkiplistNode *update[MAX_LEVEL];
    SkiplistNode *curr = obj->head;
    for (int i = obj->level - 1; i >= 0; i--) {
        /* 找到第 i 层小于且最接近 num 的元素*/
        while (curr->forward[i] && curr->forward[i]->val < num) {
            curr = curr->forward[i];
        }
        update[i] = curr;
    }
    curr = curr->forward[0];
    /* 如果值不存在则返回 false */
    if (!curr || curr->val != num) {
        return false;
    }
    for (int i = 0; i < obj->level; i++) {
        if (update[i]->forward[i] != curr) {
            break;
        } 
        /* 对第 i 层的状态进行更新,将 forward 指向被删除节点的下一跳 */
        update[i]->forward[i] = curr->forward[i];
    }
    skiplistNodeFree(curr);
    /* 更新当前的 level */
    while (obj->level > 1 && obj->head->forward[obj->level - 1] == NULL) {
        obj->level--;
    }
    return true;
}

void skiplistFree(Skiplist* obj) {
    for (SkiplistNode * curr = obj->head; curr; ) {
        SkiplistNode *prev = curr;
        curr = curr->forward[0];
        skiplistNodeFree(prev);
    }
    free(obj);
}

 

<think>好的,用户想了解跳表的设计原理和实现方法。我需要先回顾一下跳表的基本概念和结构。跳表是一种概率数据结构,允许快速查找、插入和删除操作,时间复杂度是O(log n)。它通过多链表来实现,每一都是下一的“快速通道”。 首先,我应该解释跳表的设计思想。用户可能知道链表,但普通链表的查找效率低,跳表通过添加索引来提升效率。需要说明每个节点有多个指针,指向不同的后续节点。比如,最底是完整链表,上是索引节点数量逐减少,这样在查找时可以跳过大量节点。 接下来是具体实现步骤。需要描述节点的结构,比如包含和多个向前的指针。然后说明如何随机生成节点数,通常使用概率方法,比如抛硬币,每次有50%的概率增加数,直到失败为止。这样可以保证高数的节点较少,形成类似金字塔的结构。 然后需要详细说明插入、删除和查找操作的逻辑。比如,插入时先找到合适的位置,然后随机生成数,再在各中插入节点。查找则是从最高开始,向右移动直到下一个节点大于目标,然后下降到下一继续,直到找到或到达底。 可能用户对跳表的性能分析感兴趣,但问题主要集中在设计与实现上,所以需要保持重点。不过可以简要提到时间复杂度,并引用已有的资料,比如参考中提到的O(log n)时间完成操作,以及和红黑树的比较。 用户提供的引用资料中有关于跳表在Redis中的应用,以及与其他数据结构的对比。可以适当引用这些内容来增强回答的权威性,比如引用[2]和[3]说明跳表的性能优势,引用[4]说明其作为查找结构的应用。 需要注意回答的结构清晰,分点说明设计原理和实现步骤。可能需要用代码示例来展示节点结构和关键操作,比如Python中的实现,但根据用户提供的示例,他们可能希望看到代码块。同时,根据系统级指令,数学表达式要用$...$,比如讨论时间复杂度时写成$O(\log n)$。 最后,生成的相关问题要围绕跳表的应用、优化和与其他数据结构的比较,确保问题与主题相关,帮助用户进一步学习。比如,跳表的时间复杂度证明、与平衡树的比较,以及在Redis中的具体应用。</think>### 跳表数据结构设计与实现 #### 一、设计原理 跳表(Skip List)是一种基于**概率平衡**的多链表结构,核心思想是通过构建多索引加速数据查找。其设计灵感来源于对有序链表的改进,通过增加索引将查找时间复杂度从$O(n)$优化到$O(\log n)$[^3]。 1. **级结构** 跳表包含多个级,最底(第0)是完整的**有序链表**,上都是下的"快速通道"(稀疏索引)。例如: ``` 第31 ----------------> 9 第21 ------> 5 ------->9 第11 ->3 ->5 ->7 ->9 第01,2,3,4,5,6,7,8,9 ``` 2. **节点结构** 每个节点包含: - 存储的(或键对) - 多个**向前指针数组**,指向不同的下一个节点 例如节点结构定义: ```python class SkipListNode: def __init__(self, value, level): self.value = value self.forward = [None] * (level + 1) # 各指针 ``` 3. **随机数生成** 新节点插入时通过**抛硬币算法**随机生成数: - 初始数$level=0$ - 以概率$p$(通常取0.5)增加数,直到随机数不满足条件 这使得高节点指数级减少,形成金字塔结构[^2]。 #### 二、关键操作实现 ##### 1. 查找操作 从最高开始向右遍历,若下一节点大于目标则**向下一**,直到找到目标或到达底: ```python def search(skip_list, target): current = skip_list.header for i in reversed(range(skip_list.max_level)): while current.forward[i] and current.forward[i].value < target: current = current.forward[i] current = current.forward[0] return current if current and current.value == target else None ``` ##### 2. 插入操作 1. 查找插入位置并记录各的前驱节点 2. 随机生成新节点数 3. 更新各指针链 ```python def insert(skip_list, value): update = [None] * (skip_list.max_level + 1) current = skip_list.header # 查找插入位置 for i in reversed(range(skip_list.max_level)): while current.forward[i] and current.forward[i].value < value: current = current.forward[i] update[i] = current # 生成随机数 new_level = random_level() if new_level > skip_list.max_level: update.extend([skip_list.header] * (new_level - skip_list.max_level)) skip_list.max_level = new_level # 创建新节点并更新指针 new_node = SkipListNode(value, new_level) for i in range(new_level): new_node.forward[i] = update[i].forward[i] update[i].forward[i] = new_node ``` ##### 3. 删除操作 类似插入过程,需找到待删除节点在各的前驱节点后更新指针链。 #### 三、性能优势 1. **时间复杂度** - 查找/插入/删除:$O(\log n)$(通过索引快速跳转) - 优于普通链表,与平衡树(如红黑树)相当 2. **实现复杂度** 代码量比红黑树减少约30%-50%,且无需复杂的旋转操作[^3] 3. **范围查询** 支持$O(\log n + k)$时间复杂度的范围查询(k为结果数量),优于平衡树[^4] ```python # 跳表完整类定义示例 import random class SkipList: def __init__(self, p=0.5, max_level=16): self.header = SkipListNode(None, max_level) self.p = p self.max_level = max_level self.level = 0 def random_level(self): level = 0 while random.random() < self.p and level < self.max_level: level += 1 return level ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值