跳表的简单实现

跳表(SkipList)是一种检索效率非常高的数据结构,其检索效率经证明与红黑树相当。但是,轮到实现复杂度比较的时候,跳表可就把红黑树、AVL树等结构足足甩出了八条街,以至于LevelDB、Redis等著名的工程项目当中,都采取了跳表的实现方案。
另外,在并发环境下,SkipList相对于经典树形结构还有一点优势。红黑树在插入和删除的时候可能会有涉及到树中多节点的rebalance操作,而SkipList的变化操作则会更加局部性一些,需要被加锁的节点更少,因此在并发环境下理论上性能会更好一些。
本质上,跳表是一种添加了多级“索引”的链表,采用了空间换时间的方式,通过随机的方式决定节点的层级数,在节点的层级上均维护该节点的连接节点,即,将连接指针变成了一个数组。

跳表的简易实现代码如下:

  
// 跳表实现
  
#include <stdio.h>  
#include <assert.h>  
#include <stdlib.h>  
#include <time.h>  
  
/* 跳表节点数据结构 
 * 
 */  
template <typename TData>  
struct SkipListNode  
{  
    TData value; // 节点值  
    SkipListNode<TData>** forward;  // 节点前向指针数组  
};  
  
/* 跳表操作的返回码 
 */  
enum  
{  
    SKIP_LIST_TMPL_INSERT_NODE_SUCCESS = 0,                        // 插入节点成功  
    SKIP_LIST_TMPL_GET_FREE_SKIP_LIST_NODE_FAILED = 1,     // 获取空闲节点失败  
    SKIP_LIST_TMPL_NODE_EXIST = 2,                         // 节点已经存在  
    SKIP_LIST_TMPL_NODE_NOT_EXIST = 3,                     // 节点不存在于跳表中  
    SKIP_LIST_TMPL_DELETE_NODE_SUCCESS = 4,                   // 删除节点成功  
};  
  
/* 跳表 
 * 
 */  
template <typename TData>  
class SkipListTmpl  
{  
public:  
    /* 初始化跳表 
     */  
    bool InitSkipListTmpl();  
  
    /* 获取一个空闲的跳表节点 
     * @param level  跳表节点的层数 
     * @return 返回NULL表示创建失败,否则返回创建的跳表节点指针 
     */  
    SkipListNode<TData>* GetFreeSkipListNode(int level);  
  
    /* 增加一个跳表节点 
     * @param value 节点值 
     * @return 返回操作码,详情看上面跳表操作返回码的意义 
     */  
    int InsertSkipListNode(const TData &value);  
  
    /* 删除一个跳表节点 
     * @param value 节点值 
     * @return 返回操作码,详情看上面跳表操作返回码的意义 
     */  
    int DeleteSkipListNode(const TData &value);  
  
    /* 查找跳表节点 
     * @param value 节点值 
     * @return 返回NULL表示查找失败,否则返回找到了跳表节点指针 
     */  
    SkipListNode<TData>* SearchSkipListNode(const TData &value);  
  
    /* 遍历处理跳表中所有节点 
     * @param proc 回调的处理函数 
     * @return 返回处理的节点数目 
     */  
    int ProcessEverySkipListNode(void (*proc)(const TData &value));  
  
private:  
    int level; // 当前跳表的最大层数  
    SkipListNode<TData>* header;  // 跳表头节点  
  
private:  
    const static int MAX_SKIP_LIST_TMPL_LEVEL = 32;  // 跳表的最大层数  
    const static double SKIP_LIST_TMPL_LEVEL_PROBABILITY;  // i-1层节点属于i层的概率  
  
private:  
    /* 获取一个节点的层数,层数范围 
     * @return 节点的层数 
     */  
    int GetRandomSkipListNodeLevel();  
};  
  
// i-1层节点属于i层的概率  
template <typename TData>  
const double SkipListTmpl<TData>::SKIP_LIST_TMPL_LEVEL_PROBABILITY = 0.5;  
  
/* 创建一个空的跳表节点 
 * @param level  跳表节点的层数 
 * @return 返回NULL表示创建失败,否则返回创建的跳表节点指针 
 */  
template <typename TData>  
SkipListNode<TData>* SkipListTmpl<TData>::GetFreeSkipListNode(int level)  
{  
    // 节点层数范围0  
    assert(level >= 0 && level < MAX_SKIP_LIST_TMPL_LEVEL);  
  
    SkipListNode<TData>* newNode = (SkipListNode<TData>*)malloc(sizeof(SkipListNode<TData>));  
    if (NULL == newNode)  
    {  
        return NULL;  
    }  
      
    // 前向节点初始化为NULL  
    newNode->forward = (SkipListNode<TData> **)malloc(sizeof(SkipListNode<TData>*) * (level+1));  
    if (NULL == newNode->forward)  
    {  
  
        // 申请内存失败那么就归还之前申请的内存  
        free(newNode);  
        return NULL;  
    }  
      
    for (int i = 0; i <= level; ++i)  
    {  
        newNode->forward[i] = NULL;  
    }  
  
    // 创建好的跳表节点  
    return newNode;  
}  
  
/* 初始化跳表 
 */  
template <typename TData>  
bool SkipListTmpl<TData>::InitSkipListTmpl()  
{  
    level = 0; // 当前最大层初始为0  
      
    // 头结点的层数为跳表最大层数,这个节点是个冗余节点,增加它是为了方便链表操作  
    header = GetFreeSkipListNode(MAX_SKIP_LIST_TMPL_LEVEL-1);  
  
    // 初始化成功  
    if (header)  
    {  
        return true;  
    }  
  
    // 初始化失败  
    return false;  
}  
  
/* 增加一个跳表节点 
 * @param value 节点值 
 * @return 返回操作码,详情看上面跳表操作返回码的意义 
 */  
template <typename TData>  
int SkipListTmpl<TData>::InsertSkipListNode(const TData &value)  
{  
    // 保证跳表已经初始化  
    assert(NULL != header);  
  
    // 首先查找节点的插入位置  
    SkipListNode<TData>* update[MAX_SKIP_LIST_TMPL_LEVEL];  // 插入节点的前驱节点数组  
  
    SkipListNode<TData>* p = header;  
    SkipListNode<TData>* q = NULL;  
  
    // 找到跳表中插入节点的前驱节点  
    for (int k = level; k >= 0; --k)  
    {  
        q = p->forward[k];  
        while(q && q->value < value)  
        {  
            p = q;  
            q = q->forward[k];  
        }  
        update[k] = p;  
    }  
  
    // 说明节点已经存在  
    if (q && value == q->value)  
    {  
        return SKIP_LIST_TMPL_NODE_EXIST;  
    }  
  
    // 随机获取到插入节点的层数  
    int nodelevel = GetRandomSkipListNodeLevel();  
  
    // 如果插入节点层数大于当前跳表的最大层数,需要更新插入节点的前驱节点数组  
    while(nodelevel > level)  
    {  
        ++level;  
        update[level] = header;  
    }  
  
    // 获取一个空闲的跳表节点  
    SkipListNode<TData>* freeNode = GetFreeSkipListNode(nodelevel);  
    if (NULL == freeNode)  
    {  
        // 获取空闲节点失败  
        return SKIP_LIST_TMPL_GET_FREE_SKIP_LIST_NODE_FAILED;  
    }  
  
    // 初始化该节点  
    freeNode->value = value;  
  
    // 将节点插入到跳表中,update数组中保存了节点的不同层的前驱节点数组  
    for (int k = 0; k <= nodelevel; ++k)  
    {  
        freeNode->forward[k] = update[k]->forward[k];  
        update[k]->forward[k] = freeNode;  
    }  
  
    return SKIP_LIST_TMPL_INSERT_NODE_SUCCESS;  
}  
  
/* 删除一个跳表节点 
 * @param value 节点值 
 * @return 返回操作码,详情看上面跳表操作返回码的意义 
 */  
template <typename TData>  
int SkipListTmpl<TData>::DeleteSkipListNode(const TData &value)  
{  
    // 保证跳表已经初始化  
    assert(NULL != header);  
      
    // 首先查找节点的前驱节点数组  
    SkipListNode<TData>* update[MAX_SKIP_LIST_TMPL_LEVEL];  // 删除节点的前驱节点数组  
      
    SkipListNode<TData>* p = header;  
    SkipListNode<TData>* q = NULL;  
  
    // 找到跳表中删除节点的前驱节点  
    for (int k = level; k >= 0; --k)  
    {  
        q = p->forward[k];  
        while(q && q->value < value)  
        {  
            p = q;  
            q = q->forward[k];  
        }  
        update[k] = p;  
    }  
  
    // 说明删除节点存在  
    if (q && q->value == value)  
    {  
        for (int k = 0; k <= level && update[k]->forward[k] == q; ++k)  
        {  
            update[k]->forward[k] = q->forward[k];  
        }  
        free(q->forward);  
        free(q);  
  
        // 如果q节点刚好是跳表中的最大层节点,需要更新当前跳表的最大层  
        while(NULL == header->forward[level] && level > 0)  
        {  
            --level;  
        }  
  
        // 删除节点成功  
        return SKIP_LIST_TMPL_INSERT_NODE_SUCCESS;  
    }  
    else  
    {  
        // 说明删除节点不存在  
        return SKIP_LIST_TMPL_NODE_NOT_EXIST;  
    }  
}  
  
/* 查找跳表节点 
 * @param value 节点值 
 * @return 返回NULL表示查找失败,否则返回找到了跳表节点指针 
 */  
template <typename TData>  
SkipListNode<TData>* SkipListTmpl<TData>::SearchSkipListNode(const TData &value)  
{  
    // 确保跳表已经初始化  
    assert(NULL != header);  
  
    SkipListNode<TData>* p = header;  
    SkipListNode<TData>* q = NULL;  
      
    for (int k = level; k >= 0; --k)  
    {  
        q = p->forward[k];  
        while(q && q->value < value)  
        {  
            p = q;  
            q = q->forward[k];  
        }  
    }  
  
    // 说明找到节点了  
    if (q && q->value == value)  
    {  
        return q;  
    }  
    else  
    {  
        // 说明没有找到节点  
        return NULL;  
    }  
}  
  
/* 遍历处理跳表中所有节点 
 * @param proc 回调的处理函数 
 * @return 返回处理的节点数目 
 */  
template <typename TData>  
int SkipListTmpl<TData>::ProcessEverySkipListNode(void (*proc)(const TData &value))  
{  
    // 确保跳表已经初始化  
    assert(NULL != header);  
      
    int cnt = 0;  
    SkipListNode<TData>* p = header->forward[0];  
    while(p)  
    {  
        proc(p->value);  
        cnt++;  
        p = p->forward[0];  
    }  
    return cnt;  
}  
  
  
/* 随机获取一个节点的层数,层数范围0 - (MAX_SKIP_LIST_TMPL_LEVEL-1) 
 * @return 节点的层数 
 */  
template <typename TData>  
int SkipListTmpl<TData>::GetRandomSkipListNodeLevel()  
{  
    // 选择一个种子值  
    static bool seedInit = false;  
    if (!seedInit)  
    {  
        srand(time(NULL));  
        seedInit = true;  
    }  
      
    // 根据概率计算出节点的层数  
    int newLevel = 0;  
    while(1)  
    {  
        double curP = (double)(rand() % 100) / 100.0;  
        if (curP < SKIP_LIST_TMPL_LEVEL_PROBABILITY)  
        {  
            newLevel++;  
        }  
        else  
        {  
            break;  
        }  
  
        // 最大层数是MAX_SKIP_LIST_TMPL_LEVEL-1  
        if ( (MAX_SKIP_LIST_TMPL_LEVEL-1) <= newLevel)  
        {  
            break;  
        }  
    }  
      
    // 最大层数是MAX_SKIP_LIST_TMPL_LEVEL-1  
    if (newLevel >= MAX_SKIP_LIST_TMPL_LEVEL)  
    {  
        newLevel = MAX_SKIP_LIST_TMPL_LEVEL - 1;  
    }  
  
    return newLevel;  
}  

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345