python實(shí)現(xiàn)跳躍表(SkipList)

跳躍表是一種隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),目前開源軟件 Redis 和 LevelDB 都有用到它述雾,它的效率和紅黑樹以及 AVL 樹不相上下驳庭,但原理相當(dāng)簡單,只要你能熟練操作鏈表柔纵,就能輕松實(shí)現(xiàn)一個(gè)跳躍表缔杉。

skip_list

從圖中可以看到, 跳躍表主要由以下部分構(gòu)成:

  • 表頭(head):負(fù)責(zé)維護(hù)跳躍表的節(jié)點(diǎn)指針搁料。
  • 跳躍表節(jié)點(diǎn):保存著元素值或详,以及多個(gè)層。
  • 層:保存著指向其他元素的指針郭计。高層的指針越過的元素?cái)?shù)量大于等于低層的指針霸琴,為了提高查找的效率,程序總是從高層先開始訪問昭伸,然后隨著元素值范圍的縮小梧乘,慢慢降低層次。
  • 表尾:全部由 NULL 組成庐杨,表示跳躍表的末尾选调。

跳躍表有如下特點(diǎn):

  1. 每個(gè)跳躍表由很多層結(jié)構(gòu)組成。
  2. 每一層都是一個(gè)有序鏈表灵份,且第一個(gè)節(jié)點(diǎn)是頭節(jié)點(diǎn)仁堪。
  3. 最底層的有序鏈表包含所有節(jié)點(diǎn)。
  4. 每個(gè)節(jié)點(diǎn)可能有多個(gè)指針填渠,這與節(jié)點(diǎn)所包含的層數(shù)有關(guān)弦聂。
  5. 跳躍表的查找、插入氛什、刪除的時(shí)間復(fù)雜度均為O(log N)莺葫。

代碼實(shí)現(xiàn):

import random
MAX_DEPTH = 5

class SkipNode:
    def __init__(self, height = 0, elem = None):
        self.elem = elem
        self.next = [None]*height

    def __repr__(self):
        return str(self.elem)

class SkipList:
    def __init__(self):
        self.head = SkipNode()

    def updateList(self, elem):

        update = [None] * len(self.head.next)
        x = self.head

        for i in reversed(range(len(self.head.next))):
            while x.next[i] != None and \
                    x.next[i].elem < elem:
                x = x.next[i]
            update[i] = x

        return update

    def find(self, elem, update=None):
        if update == None:
            update = self.updateList(elem)
        if len(update) > 0:
            candidate = update[0].next[0]
            if candidate != None and candidate.elem == elem:
                return candidate
        return None

    def insert(self, elem):
        node = SkipNode(self.randomHeight(), elem)
        
        while len(self.head.next) < len(node.next):
            self.head.next.append(None)

        update = self.updateList(elem)
        if self.find(elem, update) == None:
            for i in range(len(node.next)):
                node.next[i] = update[i].next[i]
                update[i].next[i] = node

    def randomHeight(self):
        k = 1
        while random.randint(0, 1):
            k = k + 1
            if k == MAX_DEPTH:
                break
        return k

    def remove(self, elem):
        update = self.updateList(elem)
        x = self.find(elem, update)
        if x != None:
            for i in range(len(x.next)):
                update[i].next[i] = x.next[i]
                if self.head.next[i] == None:
                    self.head.next = self.head.next[:i]
                    return

    def traversal(self):
        for i in reversed(range(len(self.head.next))):
            x = self.head
            line = []
            while x.next[i] != None:
                line.append(str(x.next[i].elem))
                x = x.next[i]
            print('line{}: '.format(i+1) + '->'.join(line))

主要方法updateList的作用是,從跳躍表的最頂層開始依次向下查找枪眉,找到該層級中比給定元素elem小的最大一個(gè)元素捺檬,將該元素保存起來,重復(fù)以上步驟知道到達(dá)最底層瑰谜。它返回一個(gè)列表update欺冀,update[0]表示第一層最后一個(gè)比elem小的元素,以此類推萨脑。該方法可以使得插入刪除操作變得更加簡單隐轩。

在向跳躍表中插入新的結(jié)點(diǎn)時(shí)候,我們需要生成該結(jié)點(diǎn)的層數(shù)渤早。使用拋硬幣的思想隨機(jī)生成層數(shù)职车,如果是正面(random.randint(0, 1) == 1)則層數(shù)加一,直到拋出反面為止鹊杖。其中的 MAX_DEPTH 是防止如果運(yùn)氣太好悴灵,層數(shù)就會(huì)太高,而太高的層數(shù)往往并不會(huì)提供額外的性能骂蓖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末积瞒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子登下,更是在濱河造成了極大的恐慌茫孔,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件被芳,死亡現(xiàn)場離奇詭異缰贝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)畔濒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門剩晴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侵状,你說我怎么就攤上這事赞弥。” “怎么了壹将?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵嗤攻,是天一觀的道長。 經(jīng)常有香客問我诽俯,道長妇菱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任暴区,我火速辦了婚禮闯团,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仙粱。我一直安慰自己房交,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布伐割。 她就那樣靜靜地躺著候味,像睡著了一般刃唤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上白群,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天尚胞,我揣著相機(jī)與錄音,去河邊找鬼帜慢。 笑死笼裳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的粱玲。 我是一名探鬼主播躬柬,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抽减!你這毒婦竟也來了允青?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤胯甩,失蹤者是張志新(化名)和其女友劉穎昧廷,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體偎箫,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡木柬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淹办。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眉枕。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖怜森,靈堂內(nèi)的尸體忽然破棺而出速挑,到底是詐尸還是另有隱情,我是刑警寧澤副硅,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布姥宝,位于F島的核電站,受9級特大地震影響恐疲,放射性物質(zhì)發(fā)生泄漏腊满。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一培己、第九天 我趴在偏房一處隱蔽的房頂上張望碳蛋。 院中可真熱鬧,春花似錦省咨、人聲如沸肃弟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽笤受。三九已至穷缤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間箩兽,已是汗流浹背绅项。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留比肄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓囊陡,卻偏偏與公主長得像芳绩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子撞反,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348