skip list(跳表)

第一次看到這種數(shù)據(jù)結(jié)構(gòu)還是剛接觸ocean base架構(gòu)的時(shí)候。粗略掃了幾眼,以為是一個(gè)簡(jiǎn)單的二級(jí)索引,沒有仔細(xì)考慮就略過(guò)了。后來(lái)去北京出差,經(jīng)神夜路點(diǎn)播,遂明白這種鏈表式結(jié)構(gòu)的簡(jiǎn)約而不簡(jiǎn)單,有一種四兩撥千斤的優(yōu)雅。

Skip lists are a data structure that can be used in place of balanced trees. Skip lists use probabilistic balancing rather than strictly enforced balancing and as a result the algorithms for insertion and deletion in skip lists are much simpler and significantly faster than equivalent algorithms for balanced trees.
--William Pugh

相比于紅黑樹,B樹,AVL樹,跳表的實(shí)現(xiàn)相當(dāng)簡(jiǎn)單,同時(shí),由于其多維鏈表的特性,使得跳表可以支持無(wú)鎖的多讀一寫。(鏈表的多讀一寫無(wú)鎖實(shí)現(xiàn)方式這里就不展開了)。
不同于B樹,跳表的平衡性依靠隨機(jī)算法,在正常情況下,該結(jié)構(gòu)的查找,插入,刪除的時(shí)間復(fù)雜度都是logN。
先從一維鏈表開始,我們知道在鏈表中查找一個(gè)元素I的話,需要將整個(gè)鏈表遍歷一次。



如果是說(shuō)鏈表是排序的,并且節(jié)點(diǎn)中還存儲(chǔ)了指向前面第二個(gè)節(jié)點(diǎn)的指針的話,那么在查找一個(gè)節(jié)點(diǎn)時(shí),僅僅需要遍歷N/2個(gè)節(jié)點(diǎn)即可。



這基本上就是跳表的核心思想,其實(shí)也是一種通過(guò)“空間來(lái)?yè)Q取時(shí)間”的一個(gè)算法。
下面我們來(lái)看一個(gè)4層跳表示例:

查找時(shí)首先從高層開始查找,之后逐漸降低層次靠近數(shù)據(jù),完成定位。

插入操作:



由于跳表數(shù)據(jù)結(jié)構(gòu)整體上是有序的,所以在插入時(shí),需要首先查找到合適的位置,然后就是修改指針(和鏈表中操作類似),然后更新跳表的level變量。
補(bǔ)充一個(gè)數(shù)據(jù)節(jié)點(diǎn)層次確定算法:
int height = 1;
    
while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) 
{
     height++;
}

可以發(fā)現(xiàn)層級(jí)越高的節(jié)點(diǎn)越少,因此跳表整體的指針開銷并不高。 相比于同級(jí)別的樹形實(shí)現(xiàn),跳表具有更快的速度,更低的空間開銷和更簡(jiǎn)單的實(shí)現(xiàn)。

/*
·* skipList.h
·*
·* ·Created on: 2013年8月7日
·* · · ·Author: sigh.xy
·*/
#ifndef SKIPLIST_H_
#define SKIPLIST_H_
#include <iostream>
#include <stack>
//rand
#include<stdlib.h>
template <class Key = int, class Value = int>
class Node
{
public:
    Key key;
    Value value;
    Node(Key k, Value v) : key(k), value(v){}
    Node(){}
};
template <class Key = int, class Value = int>
class Element
{
    Node<Key, Value> node;
    Element** next;
public:
    Element() : next(NULL) {}
    Element(Node<Key, Value> node, int level)
    {
        this->node = node;
        next = new Element*[level];
        for (int i = 0; i < level; i++)
        {
            next[i] = NULL;
        }
    }
    void setNext(int place, Element* nElement)
    {
        next[place] = nElement;
    }
    Element* & getNext(int place)
    {
        return next[place];
    }
    Key getKey()
    {
        return node.key;
    }
    Value getValue()
    {
        return node.value;
    }
    ~Element()
    {
        if (next)
        {
            delete[] next;
        }
    }
};
//declare
template <class Key, class Value>
class SkipIterator;
template <class Key = int, class Value = int, int MAXLEVEL = 4>
class SkipList
{
    //head
    Element<Key, Value>** head;
    int randLevel(int level = MAXLEVEL);
    void findWay(Key key, std::stack<Element<Key, Value>** >& pStack);
public:
    typedef SkipIterator<Key, Value> Iterator;
    SkipList()
    {
        head = new Element<Key, Value>*[MAXLEVEL];
        for (int i = 0; i < MAXLEVEL; i++)
        {
            head[i] = NULL;
        }
    }
    Value find(Key key);
    bool insert(Key key, Value value);
    Iterator begin()
    {
        return head[0];
    }
    Iterator end()
    {
        return Iterator();
    }
    //another kind of insert
    //bool delKey(Key key);
    ~SkipList()
    {
        Element<Key, Value>* cur = head[0];
        while (cur)
        {
            Element<Key, Value>* tmp = cur;
            cur = cur->getNext(0);
            delete tmp;
        }
    }
};
//查找數(shù)據(jù)
template <class Key, class Value, int MAXLEVEL>
Value SkipList<Key, Value, MAXLEVEL>::find(const Key key)
{
    if (NULL == head)
    {
        return (Value) 0;
    }
    //std::cout << "ok" << std::endl;
    int rawL = MAXLEVEL - 1;
    Element<Key, Value>* cur = NULL;
    //find the first < place
    while (rawL >= 0)
    {
        if (head[rawL] && head[rawL]->getKey() == key)
        {
            return head[rawL]->getValue();
        }
        else if (head[rawL] && head[rawL]->getKey() < key)
        {
            cur = head[rawL];
            break;
        }
        rawL--;
    }
    //std::cout << "rawL = " << rawL << std::endl;
    while (rawL >= 0)
    {
        if (cur && cur->getKey() == key)
        {
            return cur->getValue();
        }
        else if (cur->getNext(rawL) && cur->getNext(rawL)->getKey() <= key)
        {
            cur = cur->getNext(rawL);
        }
        else
        {
            rawL--;
        }
    }
    return (Value) 0;
}
//通過(guò)棧記錄查找路徑,用于插入操作。
template <class Key, class Value, int MAXLEVEL>
void SkipList<Key, Value, MAXLEVEL>::findWay(Key key,
        std::stack<Element<Key, Value>** >& pStack)
{
    int rawL = MAXLEVEL - 1;
    Element<Key, Value>* cur = NULL;
    //find the first < place
    while (rawL >= 0)
    {
        if (head[rawL] && head[rawL]->getKey() < key)
        {
            cur = head[rawL];
            break;
        }
        pStack.push(&head[rawL]);
        rawL--;
    }
    while (rawL >= 0)
    {
        if (cur->getNext(rawL) && cur->getNext(rawL)->getKey() <= key)
        {
            cur = cur->getNext(rawL);
        }
        else
        {
            pStack.push(&cur->getNext(rawL));
            rawL--;
        }
    }
}
//插入操作
template <class Key, class Value, int MAXLEVEL>
bool SkipList<Key, Value, MAXLEVEL>::insert(Key key, Value value)
{
    int level = randLevel();
    Element<Key, Value>* element =
            new Element<Key, Value>(Node<Key, Value>(key, value), level);
    std::stack<Element<Key, Value>**> pStack;
    findWay(key, pStack);
    for (int i = 0; i < level; i++)
    {
        element->getNext(i) = *pStack.top();
        *(pStack.top()) = element;
        //std::cout << "head = " << head[0]->getValue() << std::endl;
        pStack.pop();
    }
    //std::cout << head[0]->getNext(0) << std::endl;
    return true;
}
template <class Key, class Value, int MAXLEVEL>
int SkipList<Key, Value, MAXLEVEL>::randLevel(int level)
{
    int height = 1;
    while (height < MAXLEVEL && ((rand() % level) == 0))
    {
        height++;
    }
    return height;
}
//iterator
template <class Key, class Value>
class SkipIterator
{
private:
    Element<Key, Value>* element;
public:
    typedef Element<Key, Value> EType;
    SkipIterator(Element<Key, Value>* e) : element(e) {}
    SkipIterator()
    {
        element = NULL;
    }
    EType& operator*()
    {
        return *element;
    }
    void operator++()
    {
        element = element->getNext(0);
    }
    void operator++(int)
    {
        ++*this;
    }
    bool operator!= (SkipIterator<Key, Value> right)
    {
        return this->element != right.element;
    }
};
#endif /* SKIPLIST_H_ */

基于模版的某種實(shí)現(xiàn)方式?jīng)]有做很嚴(yán)格的測(cè)試,因此不保證正確性。
(原文時(shí)間2013-8-5)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評(píng)論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,441評(píng)論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,211評(píng)論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評(píng)論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,475評(píng)論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,834評(píng)論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評(píng)論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,009評(píng)論 0 290
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,559評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,516評(píng)論 1 374
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評(píng)論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,728評(píng)論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評(píng)論 1 295
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,249評(píng)論 3 399
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,484評(píng)論 2 379

推薦閱讀更多精彩內(nèi)容