跳表的簡單實現(xiàn)

跳表(SkipList)是一種檢索效率非常高的數(shù)據(jù)結構,其檢索效率經(jīng)證明與紅黑樹相當。但是祭隔,輪到實現(xiàn)復雜度比較的時候,跳表可就把紅黑樹路操、AVL樹等結構足足甩出了八條街疾渴,以至于LevelDB、Redis等著名的工程項目當中屯仗,都采取了跳表的實現(xiàn)方案搞坝。
另外,在并發(fā)環(huán)境下魁袜,SkipList相對于經(jīng)典樹形結構還有一點優(yōu)勢桩撮。紅黑樹在插入和刪除的時候可能會有涉及到樹中多節(jié)點的rebalance操作敦第,而SkipList的變化操作則會更加局部性一些,需要被加鎖的節(jié)點更少店量,因此在并發(fā)環(huán)境下理論上性能會更好一些芜果。
本質上,跳表是一種添加了多級“索引”的鏈表融师,采用了空間換時間的方式右钾,通過隨機的方式?jīng)Q定節(jié)點的層級數(shù),在節(jié)點的層級上均維護該節(jié)點的連接節(jié)點旱爆,即舀射,將連接指針變成了一個數(shù)組。

跳表的簡易實現(xiàn)代碼如下:

  
// 跳表實現(xiàn)
  
#include <stdio.h>  
#include <assert.h>  
#include <stdlib.h>  
#include <time.h>  
  
/* 跳表節(jié)點數(shù)據(jù)結構 
 * 
 */  
template <typename TData>  
struct SkipListNode  
{  
    TData value; // 節(jié)點值  
    SkipListNode<TData>** forward;  // 節(jié)點前向指針數(shù)組  
};  
  
/* 跳表操作的返回碼 
 */  
enum  
{  
    SKIP_LIST_TMPL_INSERT_NODE_SUCCESS = 0,                        // 插入節(jié)點成功  
    SKIP_LIST_TMPL_GET_FREE_SKIP_LIST_NODE_FAILED = 1,     // 獲取空閑節(jié)點失敗  
    SKIP_LIST_TMPL_NODE_EXIST = 2,                         // 節(jié)點已經(jīng)存在  
    SKIP_LIST_TMPL_NODE_NOT_EXIST = 3,                     // 節(jié)點不存在于跳表中  
    SKIP_LIST_TMPL_DELETE_NODE_SUCCESS = 4,                   // 刪除節(jié)點成功  
};  
  
/* 跳表 
 * 
 */  
template <typename TData>  
class SkipListTmpl  
{  
public:  
    /* 初始化跳表 
     */  
    bool InitSkipListTmpl();  
  
    /* 獲取一個空閑的跳表節(jié)點 
     * @param level  跳表節(jié)點的層數(shù) 
     * @return 返回NULL表示創(chuàng)建失敗怀伦,否則返回創(chuàng)建的跳表節(jié)點指針 
     */  
    SkipListNode<TData>* GetFreeSkipListNode(int level);  
  
    /* 增加一個跳表節(jié)點 
     * @param value 節(jié)點值 
     * @return 返回操作碼脆烟,詳情看上面跳表操作返回碼的意義 
     */  
    int InsertSkipListNode(const TData &value);  
  
    /* 刪除一個跳表節(jié)點 
     * @param value 節(jié)點值 
     * @return 返回操作碼,詳情看上面跳表操作返回碼的意義 
     */  
    int DeleteSkipListNode(const TData &value);  
  
    /* 查找跳表節(jié)點 
     * @param value 節(jié)點值 
     * @return 返回NULL表示查找失敗空镜,否則返回找到了跳表節(jié)點指針 
     */  
    SkipListNode<TData>* SearchSkipListNode(const TData &value);  
  
    /* 遍歷處理跳表中所有節(jié)點 
     * @param proc 回調的處理函數(shù) 
     * @return 返回處理的節(jié)點數(shù)目 
     */  
    int ProcessEverySkipListNode(void (*proc)(const TData &value));  
  
private:  
    int level; // 當前跳表的最大層數(shù)  
    SkipListNode<TData>* header;  // 跳表頭節(jié)點  
  
private:  
    const static int MAX_SKIP_LIST_TMPL_LEVEL = 32;  // 跳表的最大層數(shù)  
    const static double SKIP_LIST_TMPL_LEVEL_PROBABILITY;  // i-1層節(jié)點屬于i層的概率  
  
private:  
    /* 獲取一個節(jié)點的層數(shù)浩淘,層數(shù)范圍 
     * @return 節(jié)點的層數(shù) 
     */  
    int GetRandomSkipListNodeLevel();  
};  
  
// i-1層節(jié)點屬于i層的概率  
template <typename TData>  
const double SkipListTmpl<TData>::SKIP_LIST_TMPL_LEVEL_PROBABILITY = 0.5;  
  
/* 創(chuàng)建一個空的跳表節(jié)點 
 * @param level  跳表節(jié)點的層數(shù) 
 * @return 返回NULL表示創(chuàng)建失敗,否則返回創(chuàng)建的跳表節(jié)點指針 
 */  
template <typename TData>  
SkipListNode<TData>* SkipListTmpl<TData>::GetFreeSkipListNode(int level)  
{  
    // 節(jié)點層數(shù)范圍0  
    assert(level >= 0 && level < MAX_SKIP_LIST_TMPL_LEVEL);  
  
    SkipListNode<TData>* newNode = (SkipListNode<TData>*)malloc(sizeof(SkipListNode<TData>));  
    if (NULL == newNode)  
    {  
        return NULL;  
    }  
      
    // 前向節(jié)點初始化為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;  
    }  
  
    // 創(chuàng)建好的跳表節(jié)點  
    return newNode;  
}  
  
/* 初始化跳表 
 */  
template <typename TData>  
bool SkipListTmpl<TData>::InitSkipListTmpl()  
{  
    level = 0; // 當前最大層初始為0  
      
    // 頭結點的層數(shù)為跳表最大層數(shù)吴攒,這個節(jié)點是個冗余節(jié)點张抄,增加它是為了方便鏈表操作  
    header = GetFreeSkipListNode(MAX_SKIP_LIST_TMPL_LEVEL-1);  
  
    // 初始化成功  
    if (header)  
    {  
        return true;  
    }  
  
    // 初始化失敗  
    return false;  
}  
  
/* 增加一個跳表節(jié)點 
 * @param value 節(jié)點值 
 * @return 返回操作碼,詳情看上面跳表操作返回碼的意義 
 */  
template <typename TData>  
int SkipListTmpl<TData>::InsertSkipListNode(const TData &value)  
{  
    // 保證跳表已經(jīng)初始化  
    assert(NULL != header);  
  
    // 首先查找節(jié)點的插入位置  
    SkipListNode<TData>* update[MAX_SKIP_LIST_TMPL_LEVEL];  // 插入節(jié)點的前驅節(jié)點數(shù)組  
  
    SkipListNode<TData>* p = header;  
    SkipListNode<TData>* q = NULL;  
  
    // 找到跳表中插入節(jié)點的前驅節(jié)點  
    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;  
    }  
  
    // 說明節(jié)點已經(jīng)存在  
    if (q && value == q->value)  
    {  
        return SKIP_LIST_TMPL_NODE_EXIST;  
    }  
  
    // 隨機獲取到插入節(jié)點的層數(shù)  
    int nodelevel = GetRandomSkipListNodeLevel();  
  
    // 如果插入節(jié)點層數(shù)大于當前跳表的最大層數(shù)洼怔,需要更新插入節(jié)點的前驅節(jié)點數(shù)組  
    while(nodelevel > level)  
    {  
        ++level;  
        update[level] = header;  
    }  
  
    // 獲取一個空閑的跳表節(jié)點  
    SkipListNode<TData>* freeNode = GetFreeSkipListNode(nodelevel);  
    if (NULL == freeNode)  
    {  
        // 獲取空閑節(jié)點失敗  
        return SKIP_LIST_TMPL_GET_FREE_SKIP_LIST_NODE_FAILED;  
    }  
  
    // 初始化該節(jié)點  
    freeNode->value = value;  
  
    // 將節(jié)點插入到跳表中署惯,update數(shù)組中保存了節(jié)點的不同層的前驅節(jié)點數(shù)組  
    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;  
}  
  
/* 刪除一個跳表節(jié)點 
 * @param value 節(jié)點值 
 * @return 返回操作碼,詳情看上面跳表操作返回碼的意義 
 */  
template <typename TData>  
int SkipListTmpl<TData>::DeleteSkipListNode(const TData &value)  
{  
    // 保證跳表已經(jīng)初始化  
    assert(NULL != header);  
      
    // 首先查找節(jié)點的前驅節(jié)點數(shù)組  
    SkipListNode<TData>* update[MAX_SKIP_LIST_TMPL_LEVEL];  // 刪除節(jié)點的前驅節(jié)點數(shù)組  
      
    SkipListNode<TData>* p = header;  
    SkipListNode<TData>* q = NULL;  
  
    // 找到跳表中刪除節(jié)點的前驅節(jié)點  
    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;  
    }  
  
    // 說明刪除節(jié)點存在  
    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節(jié)點剛好是跳表中的最大層節(jié)點镣隶,需要更新當前跳表的最大層  
        while(NULL == header->forward[level] && level > 0)  
        {  
            --level;  
        }  
  
        // 刪除節(jié)點成功  
        return SKIP_LIST_TMPL_INSERT_NODE_SUCCESS;  
    }  
    else  
    {  
        // 說明刪除節(jié)點不存在  
        return SKIP_LIST_TMPL_NODE_NOT_EXIST;  
    }  
}  
  
/* 查找跳表節(jié)點 
 * @param value 節(jié)點值 
 * @return 返回NULL表示查找失敗极谊,否則返回找到了跳表節(jié)點指針 
 */  
template <typename TData>  
SkipListNode<TData>* SkipListTmpl<TData>::SearchSkipListNode(const TData &value)  
{  
    // 確保跳表已經(jīng)初始化  
    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];  
        }  
    }  
  
    // 說明找到節(jié)點了  
    if (q && q->value == value)  
    {  
        return q;  
    }  
    else  
    {  
        // 說明沒有找到節(jié)點  
        return NULL;  
    }  
}  
  
/* 遍歷處理跳表中所有節(jié)點 
 * @param proc 回調的處理函數(shù) 
 * @return 返回處理的節(jié)點數(shù)目 
 */  
template <typename TData>  
int SkipListTmpl<TData>::ProcessEverySkipListNode(void (*proc)(const TData &value))  
{  
    // 確保跳表已經(jīng)初始化  
    assert(NULL != header);  
      
    int cnt = 0;  
    SkipListNode<TData>* p = header->forward[0];  
    while(p)  
    {  
        proc(p->value);  
        cnt++;  
        p = p->forward[0];  
    }  
    return cnt;  
}  
  
  
/* 隨機獲取一個節(jié)點的層數(shù),層數(shù)范圍0 - (MAX_SKIP_LIST_TMPL_LEVEL-1) 
 * @return 節(jié)點的層數(shù) 
 */  
template <typename TData>  
int SkipListTmpl<TData>::GetRandomSkipListNodeLevel()  
{  
    // 選擇一個種子值  
    static bool seedInit = false;  
    if (!seedInit)  
    {  
        srand(time(NULL));  
        seedInit = true;  
    }  
      
    // 根據(jù)概率計算出節(jié)點的層數(shù)  
    int newLevel = 0;  
    while(1)  
    {  
        double curP = (double)(rand() % 100) / 100.0;  
        if (curP < SKIP_LIST_TMPL_LEVEL_PROBABILITY)  
        {  
            newLevel++;  
        }  
        else  
        {  
            break;  
        }  
  
        // 最大層數(shù)是MAX_SKIP_LIST_TMPL_LEVEL-1  
        if ( (MAX_SKIP_LIST_TMPL_LEVEL-1) <= newLevel)  
        {  
            break;  
        }  
    }  
      
    // 最大層數(shù)是MAX_SKIP_LIST_TMPL_LEVEL-1  
    if (newLevel >= MAX_SKIP_LIST_TMPL_LEVEL)  
    {  
        newLevel = MAX_SKIP_LIST_TMPL_LEVEL - 1;  
    }  
  
    return newLevel;  
}  

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末安岂,一起剝皮案震驚了整個濱河市轻猖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌域那,老刑警劉巖咙边,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異次员,居然都是意外死亡败许,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門淑蔚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來市殷,“玉大人,你說我怎么就攤上這事刹衫。” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵差牛,是天一觀的道長。 經(jīng)常有香客問我柿究,道長,這世上最難降的妖魔是什么黄选? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任蝇摸,我火速辦了婚禮,結果婚禮上办陷,老公的妹妹穿的比我還像新娘貌夕。我一直安慰自己,他們只是感情好民镜,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布啡专。 她就那樣靜靜地躺著,像睡著了一般制圈。 火紅的嫁衣襯著肌膚如雪们童。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天鲸鹦,我揣著相機與錄音慧库,去河邊找鬼。 笑死馋嗜,一個胖子當著我的面吹牛齐板,可吹牛的內容都是我干的。 我是一名探鬼主播葛菇,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼甘磨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了眯停?” 一聲冷哼從身側響起济舆,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎莺债,沒想到半個月后滋觉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡九府,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年椎瘟,在試婚紗的時候發(fā)現(xiàn)自己被綠了覆致。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侄旬。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖煌妈,靈堂內的尸體忽然破棺而出儡羔,到底是詐尸還是另有隱情宣羊,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布汰蜘,位于F島的核電站仇冯,受9級特大地震影響,放射性物質發(fā)生泄漏族操。R本人自食惡果不足惜苛坚,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望色难。 院中可真熱鬧泼舱,春花似錦、人聲如沸枷莉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笤妙。三九已至冒掌,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蹲盘,已是汗流浹背股毫。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留召衔,地道東北人皇拣。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像薄嫡,于是被迫代替她去往敵國和親氧急。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345