• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            轉(zhuǎn)自:http://www.redisbook.com 

            跳躍表

            跳躍表(skiplist)是一種隨機(jī)化的數(shù)據(jù), 由 William Pugh 在論文《Skip lists: a probabilistic alternative to balanced trees》中提出, 這種數(shù)據(jù)結(jié)構(gòu)以有序的方式在層次化的鏈表中保存元素, 它的效率可以和平衡樹媲美 —— 查找、刪除、添加等操作都可以在對數(shù)期望時間下完成, 并且比起平衡樹來說, 跳躍表的實現(xiàn)要簡單直觀得多。

            以下是一個典型的跳躍表例子(圖片來自維基百科):

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

            • 表頭(head):負(fù)責(zé)維護(hù)跳躍表的節(jié)點指針。
            • 跳躍表節(jié)點:保存著元素值,以及多個層。
            • 層:保存著指向其他元素的指針。高層的指針越過的元素數(shù)量大于等于低層的指針,為了提高查找的效率,程序總是從高層先開始訪問,然后隨著元素值范圍的縮小,慢慢降低層次。
            • 表尾:全部由 NULL 組成,表示跳躍表的末尾。

            因為跳躍表的定義可以在任何一本算法或數(shù)據(jù)結(jié)構(gòu)的書中找到, 所以本章不介紹跳躍表的具體實現(xiàn)方式或者具體的算法, 而只介紹跳躍表在 Redis 的應(yīng)用、核心數(shù)據(jù)結(jié)構(gòu)和 API 。

            跳躍表的實現(xiàn)

            為了適應(yīng)自身的功能需要, Redis 基于 William Pugh 論文中描述的跳躍表進(jìn)行了以下修改:

            1. 允許重復(fù)的 score 值:多個不同的 member 的 score 值可以相同。
            2. 進(jìn)行對比操作時,不僅要檢查 score 值,還要檢查 member :當(dāng) score 值可以重復(fù)時,單靠 score 值無法判斷一個元素的身份,所遇需要連 member 域都一并檢查才行。
            3. 每個節(jié)點都帶有一個高度為 1 層的后退指針,用于從表尾方向向表頭方向迭代:當(dāng)執(zhí)行 ZREVRANGE 或 ZREVRANGEBYSCORE 這類以逆序處理有序集的命令時,就會用到這個屬性。

            這個修改版的跳躍表由 redis.h/zskiplist 結(jié)構(gòu)定義:

            typedef struct zskiplist {

                
            // 頭節(jié)點,尾節(jié)點
                struct zskiplistNode *header, *tail;

                
            // 節(jié)點數(shù)量
                unsigned long length;

                
            // 目前表內(nèi)節(jié)點的最大層數(shù)
                int level;

            } zskiplist;
            跳躍表的節(jié)點由 redis.h/zskiplistNode 定義: 
            typedef struct zskiplistNode {

                
            // member 對象
                robj *obj;

                
            // 分值
                double score;

                
            // 后退指針
                struct zskiplistNode *backward;

                
            // 層
                struct zskiplistLevel {

                    
            // 前進(jìn)指針
                    struct zskiplistNode *forward;

                    
            // 這個層跨越的節(jié)點數(shù)量
                    unsigned int span;

                } level[];

            } zskiplistNode;

            以下是操作這兩個數(shù)據(jù)結(jié)構(gòu)的 API ,它們的作用以及相應(yīng)的算法復(fù)雜度:

            函數(shù)作用復(fù)雜度
            zslCreateNode創(chuàng)建并返回一個新的跳躍表節(jié)點最壞 O(1)
            zslFreeNode釋放給定的跳躍表節(jié)點最壞 O(1)
            zslCreate創(chuàng)建并初始化一個新的跳躍表最壞 O(N)
            zslFree釋放給定的跳躍表最壞 O(N)
            zslInsert將一個包含給定 score 和 member 的新節(jié)點添加到跳躍表中最壞 O(N) 平均 O(logN)
            zslDeleteNode刪除給定的跳躍表節(jié)點最壞 O(N)
            zslDelete刪除匹配給定 member 和 score 的元素最壞 O(N) 平均 O(logN)
            zslFirstInRange找到跳躍表中第一個符合給定范圍的元素最壞 O(N) 平均 O(logN)
            zslLastInRange找到跳躍表中最后一個符合給定范圍的元素最壞 O(N) 平均 O(logN)
            zslDeleteRangeByScore刪除 score 值在給定范圍內(nèi)的所有節(jié)點最壞 O(N2)
            zslDeleteRangeByRank刪除給定排序范圍內(nèi)的所有節(jié)點最壞 O(N2)
            zslGetRank返回目標(biāo)元素在有序集中的排位最壞 O(N) 平均 O(logN)
            zslGetElementByRank根據(jù)給定排位,返回該排位上的元素節(jié)點最壞 O(N) 平均 O(logN)

            跳躍表的應(yīng)用

            和字典、鏈表或者字符串這幾種在 Redis 中大量使用的數(shù)據(jù)結(jié)構(gòu)不同, 跳躍表在 Redis 的唯一作用, 就是實現(xiàn)有序集數(shù)據(jù)類型。

            跳躍表將指向有序集的 score 值和 member 域的指針作為元素, 并以 score 值為索引, 對有序集元素進(jìn)行排序。

            舉個例子, 以下代碼就創(chuàng)建了一個帶有 3 個元素的有序集:

            redis> ZADD s 6 x 10 y 15 z
            (integer) 
            3

            redis
            > ZRANGE s 0 -1 WITHSCORES
            1"x"
            2"6"
            3"y"
            4"10"
            5"z"
            6"15"
            在底層實現(xiàn)中, Redis 為 x 、 y 和 z 三個 member 分別創(chuàng)建了三個字符串, 并為 6 、 10 和 15 分別創(chuàng)建三個 double 類型的值, 然后用一個跳躍表將這些指針有序地保存起來, 形成這樣一個跳躍表: 

            為了展示的方便, 在圖片中我們直接將 member 和 score 值包含在表節(jié)點中, 但是在實際的定義中, 因為跳躍表要和另一個實現(xiàn)有序集的結(jié)構(gòu)(字典)分享 member 和 score 值, 所以跳躍表只保存指向 member 和 score 的指針。 更詳細(xì)的信息,請參考《有序集》章節(jié)。

            小結(jié)

            • 跳躍表是一種隨機(jī)化數(shù)據(jù)結(jié)構(gòu),它的查找、添加、刪除操作都可以在對數(shù)期望時間下完成。
            • 跳躍表目前在 Redis 的唯一作用就是作為有序集類型的底層數(shù)據(jù)結(jié)構(gòu)(之一,另一個構(gòu)成有序集的結(jié)構(gòu)是字典)。
            • 為了適應(yīng)自身的需求,Redis 基于 William Pugh 論文中描述的跳躍表進(jìn)行了修改,包括:
              1. score 值可重復(fù)。
              2. 對比一個元素需要同時檢查它的 score 和 memeber 。
              3. 每個節(jié)點帶有高度為 1 層的后退指針,用于從表尾方向向表頭方向迭代。

            久久精品国产色蜜蜜麻豆| 婷婷国产天堂久久综合五月| 国产精品久久久久久五月尺| 日本免费久久久久久久网站| 午夜天堂精品久久久久| 久久99热这里只有精品66| 少妇久久久久久被弄到高潮| 久久影院亚洲一区| 久久久久99精品成人片| 久久97久久97精品免视看| 久久精品女人天堂AV麻| 久久精品国产一区二区| 久久婷婷五月综合97色直播| 久久毛片免费看一区二区三区| 久久99精品国产99久久6| 办公室久久精品| 精品一久久香蕉国产线看播放| 国内精品久久久久久久影视麻豆| 91久久精品电影| 国产福利电影一区二区三区久久久久成人精品综合 | 亚洲日本va中文字幕久久| 久久精品日日躁夜夜躁欧美| 99久久免费国产精品特黄| 18岁日韩内射颜射午夜久久成人| 久久天天躁狠狠躁夜夜不卡| 波多野结衣久久| 国产成人久久精品一区二区三区| 国产V亚洲V天堂无码久久久| 久久免费美女视频| 欧美日韩精品久久久免费观看| 久久笫一福利免费导航 | 亚洲精品无码成人片久久| 熟妇人妻久久中文字幕| 久久久久久综合一区中文字幕| 国产视频久久| 久久免费看黄a级毛片| 国产91色综合久久免费分享| 欧美粉嫩小泬久久久久久久 | 色综合合久久天天给综看| 亚洲AV乱码久久精品蜜桃| 91精品久久久久久无码|