跳表实现(C语言)

以下是关于跳表(Skip List)的详细介绍,包括概念、原理、用途以及使用C语言实现的代码示例(带有注释):

概念

跳表是一种有序的数据结构,它通过在原始的有序链表基础上,增加多级索引来提升查找、插入和删除等操作的效率,其在功能上可以类比平衡二叉树(如红黑树等),但实现相对更简单直观。

原理

• 跳表基于概率构建多级索引。最底层是一个完整的有序链表,存储了所有的元素。然后在这个链表之上,每隔几个节点(这个间隔通常按照一定概率确定,比如随机决定一个节点是否提升为上一级索引节点)就抽出一些节点组成上一级索引链表,依次类推构建多级索引链表。

• 在查找元素时,就可以先从最顶层的索引链表开始查找,快速定位到大致范围后,再逐步下到下一级索引链表或者底层链表进行更精细的查找,通过这种“跳跃”式的查找方式,减少了比较次数,提高了查找效率。

• 插入和删除操作除了要对原始链表进行相应操作外,还需要根据概率去维护各级索引链表,保证索引结构的正确性。

用途

• 常用于替代一些需要高效查找、插入和删除操作的平衡二叉树场景,例如在数据库的索引实现中,跳表可以作为一种可选的数据结构来加快数据的检索速度。

• 在一些缓存系统中,管理缓存数据的有序集合时,跳表也能发挥很好的作用,提供较快的查找和更新操作性能。

C语言实现示例

以下是一个简单的跳表实现示例,实现了基本的插入、查找和打印功能,为了方便演示,这里的跳表最大层数设置为固定值(实际应用中可动态调整),且节点提升为索引节点的概率也是简化处理的:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 定义跳表节点结构体
typedef struct SkipListNode {
    int key;
    int value;
    struct SkipListNode **forward;  // 指向各级索引链表的下一个节点的指针数组
} SkipListNode;

// 定义跳表结构体
typedef struct SkipList {
    SkipListNode *header;  // 头节点,方便操作
    int level;  // 当前跳表的最大层数
} SkipList;

// 创建跳表节点
SkipListNode* createNode(int key, int value, int level) {
    SkipListNode *node = (SkipListNode *)malloc(sizeof(SkipListNode));
    if (node == NULL) {
        return NULL;
    }
    node->key = key;
    node->value = value;
    node->forward = (SkipListNode **)malloc((level + 1) * sizeof(SkipListNode *));  // 分配指针数组空间
    for (int i = 0; i <= level; i++) {
        node->forward[i] = NULL;
    }
    return node;
}

// 创建跳表
SkipList* createSkipList(int maxLevel) {
    SkipList *list = (SkipList *)malloc(sizeof(SkipList));
    if (list == NULL) {
        return NULL;
    }
    list->header = createNode(-1, -1, maxLevel);  // 头节点的key和value设为特殊值,方便操作
    list->level = 0;
    return list;
}

// 随机生成节点的层数,这里简单模拟,实际可更精细设置概率
int randomLevel(int maxLevel) {
    int level = 0;
    while (rand() % 2 == 0 && level < maxLevel) {  // 例如以50%概率提升一层,最多到maxLevel层
        level++;
    }
    return level;
}

// 插入元素到跳表
void insert(SkipList *list, int key, int value) {
    SkipListNode *update[16];  // 用于记录每一层插入位置的前一个节点,假设最大16层,可根据实际调整
    SkipListNode *current = list->header;

    for (int i = list->level; i >= 0; i--) {  // 从最高层开始查找合适的插入位置
        while (current->forward[i]!= NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }

    current = current->forward[0];  // 到达最底层对应位置

    if (current == NULL || current->key!= key) {  // 如果不存在相同key的节点则插入新节点
        int newLevel = randomLevel(15);  // 生成新节点的层数,假设最大16层,这里可调整
        if (newLevel > list->level) {  // 如果新节点层数大于当前跳表最大层数,更新高层索引
            for (int i = list->level + 1; i <= newLevel; i++) {
                update[i] = list->header;
            }
            list->level = newLevel;
        }

        SkipListNode *newNode = createNode(key, value, newLevel);
        for (int i = 0; i <= newLevel; i++) {
            newNode->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = newNode;
        }
    }
}

// 在跳表中查找元素
SkipListNode* search(SkipList *list, int key) {
    SkipListNode *current = list->header;
    for (int i = list->level; i >= 0; i--) {  // 从最高层开始查找
        while (current->forward[i]!= NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
    }
    current = current->forward[0];  // 到达最底层对应位置
    if (current!= NULL && current->key == key) {
        return current;
    }
    return NULL;
}

// 打印跳表,用于查看跳表结构(简单按层打印节点key)
void printSkipList(SkipList *list) {
    for (int i = list->level; i >= 0; i--) {
        SkipListNode *current = list->header->forward[i];
        printf("Level %d: ", i);
        while (current!= NULL) {
            printf("%d ", current->key);
            current = current->forward[i];
        }
        printf("\n");
    }
}

int main() {
    srand((unsigned int)time(NULL));  // 初始化随机数种子
    SkipList *list = createSkipList(15);  // 创建最大层数为15的跳表

    insert(list, 1, 10);
    insert(list, 3, 30);
    insert(list, 5, 50);
    insert(list, 7, 70);

    printSkipList(list);

    SkipListNode *found = search(list, 5);
    if (found!= NULL) {
        printf("Found key 5 with value %d\n", found->value);
    }

    return 0;
}
注释解释如下:
// 定义跳表节点结构体
typedef struct SkipListNode {
    int key;  // 节点存储的键值,用于排序和查找
    int value;  // 节点对应的值,可以根据实际需求存储各种数据
    struct SkipListNode **forward;  // 指向各级索引链表的下一个节点的指针数组,通过不同索引层进行“跳跃”查找
} SkipListNode;

// 定义跳表结构体
typedef struct SkipList {
    SkipListNode *header;  // 头节点,方便操作,其本身不存储实际数据,主要用于在查找等操作中便于从最高层索引开始遍历
    int level;  // 当前跳表的最大层数,即索引的层数,初始为0,会随着插入节点可能增加
} SkipList;

// 创建跳表节点
SkipListNode* createNode(int key, int value, int level) {
    SkipListNode *node = (SkipListNode *)malloc(sizeof(SkipListNode));
    if (node == NULL) {
        return NULL;
    }
    node->key = key;
    node->value = value;
    node->forward = (SkipListNode **)malloc((level + 1) * sizeof(SkipListNode *));  // 分配指针数组空间,根据节点层数确定数组大小,用于指向各级索引下一个节点
    for (int i = 0; i <= level; i++) {
        node->forward[i] = NULL;
    }
    return node;
}

// 创建跳表
SkipList* createSkipList(int maxLevel) {
    SkipList *list = (SkipList *)malloc(sizeof(SkipList));
    if (list == NULL) {
        return NULL;
    }
    list->header = createNode(-1, -1, maxLevel);  // 头节点的key和value设为特殊值,方便操作,比如在查找时便于从最高层开始比较
    list->level = 0;
    return list;
}

// 随机生成节点的层数,这里简单模拟,实际可更精细设置概率
int randomLevel(int maxLevel) {
    int level = 0;
    while (rand() % 2 == 0 && level < maxLevel) {  // 例如以50%概率提升一层,最多到maxLevel层,通过随机方式决定节点是否成为更高级索引节点
        level++;
    }
    return level;
}

// 插入元素到跳表
void insert(SkipList *list, int key, int value) {
    SkipListNode *update[16];  // 用于记录每一层插入位置的前一个节点,假设最大16层,可根据实际调整,方便后续插入新节点时修改指针指向
    SkipListNode *current = list->header;

    for (int i = list->level; i >= 0; i--) {  // 从最高层开始查找合适的插入位置,利用高层索引快速定位大概范围
        while (current->forward[i]!= NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
        update[i] = current;
    }

    current = current->forward[0];  // 到达最底层对应位置

    if (current == NULL || current->key!= key) {  // 如果不存在相同key的节点则插入新节点
        int newLevel = randomLevel(15);  // 生成新节点的层数,假设最大16层,这里可调整,根据概率决定新节点层数
        if (newLevel > list->level) {  // 如果新节点层数大于当前跳表最大层数,更新高层索引,将高层索引的插入位置指向头节点
            for (int i = list->level + 1; i <= newLevel; i++) {
                update[i] = list->header;
            }
            list->level = newLevel;
        }

        SkipListNode *newNode = createNode(key, value, newNodeLevel);
        for (int i = 0; i <= newLevel; i++) {
            newNode->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = newNode;
        }
    }
}

// 在跳表中查找元素
SkipListNode* search(SkipList *list, int key) {
    SkipListNode *current = list->header;
    for (int i = list->level; i >= 0; i--) {  // 从最高层开始查找,利用索引快速缩小查找范围
        while (current->forward[i]!= NULL && current->forward[i]->key < key) {
            current = current->forward[i];
        }
    }
    current = current->forward[0];  // 到达最底层对应位置
    if (current!= NULL && current->key == key) {
        return current;
    }
    return NULL;
}

// 打印跳表,用于查看跳表结构(简单按层打印节点key)
void printSkipList(SkipList *list) {
    for (int i = list->level; i >= 0; i--) {
        SkipListNode *current = list->header->forward[i];
        printf("Level %d: ", i);
        while (current!= NULL) {
            printf("%d ", current->key);
            current = current->forward[i];
        }
        printf("\n");
    }
}

int main() {
    srand((unsigned int)time(NULL));  // 初始化随机数种子,确保每次运行生成不同随机结果
    SkipList *list = createSkipList(15);  // 创建最大层数为15的跳表

    insert(list, 1, 10);
    insert(list, 3, 30);
    insert(list, 5, 50);
    insert(list, 7, 70);

    printSkipList(list);

    SkipListNode *found = search(list, 5);
    if (found!= NULL) {
        printf("Found key 5 with value %d\n", found->value);
    }

    return 0;
}

请注意,上述代码只是一个简单的跳表实现示例,实际应用中的跳表还可以进一步优化,比如更合理地设置索引节点提升概率、动态调整跳表最大层数、完善错误处理以及增加更多操作方法等,不过它展示了跳表的基本原理和核心操作的实现思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请向我看齐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值