第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 后端通用面試教程 后端通用面試教程 Redis數(shù)據(jù)結(jié)構(gòu)(二)

1. 前言

上一章節(jié)介紹了 SDS 數(shù)據(jù)結(jié)構(gòu),即 Redis 最基礎(chǔ)的 Key-Value 存儲實現(xiàn),本章節(jié)繼續(xù)介紹 Redis 底層的高級數(shù)據(jù)結(jié)構(gòu)。

Redis 的五種基本結(jié)構(gòu)中還有一個叫做 zset 的數(shù)據(jù)結(jié)構(gòu),zset 保證了每個值的唯一性,這方面性質(zhì)同傳統(tǒng)的 set 集合,也可以對每個值賦予 score,按照 score 進行排序。這種高級性質(zhì)依賴于底層的跳躍表數(shù)據(jù)結(jié)構(gòu)實現(xiàn)。

2. SkipList

2.1 SkipList 數(shù)據(jù)結(jié)構(gòu)

面試官提問: Redis zset 數(shù)據(jù)結(jié)構(gòu)的底層實現(xiàn)是什么?為什么要使用跳躍表?

題目解析:

在介紹跳躍表(SkipList,簡稱跳表)之前,我們可以以單鏈表數(shù)據(jù)結(jié)構(gòu)作為對比。

圖片描述

(單鏈表結(jié)構(gòu)示意圖)

在單鏈表中,我們查詢一個元素的時間復(fù)雜度是 O (N),其中 N 是鏈表的長度,因為需要誒個遍歷節(jié)點,單鏈表不支持數(shù)組的隨機插入和刪除,也不支持數(shù)組的自動排序,顯然不適合作為 zset 的實現(xiàn)方式。

跳躍表的基礎(chǔ)定義可參考維基百科定義

參考定義,我們給出 C 語言的結(jié)構(gòu)體定義:

typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned int span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;
    unsigned long length;
    int level;
} zskiplist;

候選人需要描述跳表的數(shù)據(jù)結(jié)構(gòu),可以通過畫圖或者其他方式給出定義。

圖片描述
?

(典型的跳躍表結(jié)構(gòu),圖片來自網(wǎng)絡(luò),侵刪)

從結(jié)構(gòu)體可以看出,節(jié)點有不同的定義:

  • sds:存儲的字符串對象;
  • score:分數(shù),跳表中所有節(jié)點按照 score 由大到小排列;
  • backward:指向后退節(jié)點的指針;
  • forward:指向下一個節(jié)點的指針;
  • level:數(shù)組,數(shù)組中的每一個元素包括了指向其他節(jié)點的指針,level 的長度在 1 到 32(2^5)之間。

其中跳表的主要組成結(jié)構(gòu)有:

  • 表頭:表示跳表的入口;
  • 表尾:表示跳表的尾部,數(shù)值全部都是 NULL;
  • 節(jié)點:保存具體數(shù)值,并且具有層結(jié)構(gòu);
  • 層:就是上述 level 定義中的單個元素,保存前一個節(jié)點的指針,以及該層下個節(jié)點向前跨越的數(shù)值(span)。

跳表的查詢過程本質(zhì)上是自上而下的二分查找,插入和查詢過程都相對復(fù)雜,這里不做贅述。

在闡述基本定義之后,我們需要關(guān)注跳躍表的核心特點:

  • 本質(zhì)是隨機化數(shù)據(jù)結(jié)構(gòu),可以在對數(shù)(logN)時間內(nèi)完成對數(shù)據(jù)的查找、插入、刪除操作;
  • 跳躍表在 Redis 的唯一應(yīng)用,就是作為有序集合支撐底層數(shù)據(jù)結(jié)構(gòu)。

2.2 跳躍表 vs 二叉樹

面試官提問: 二叉查找樹也能實現(xiàn)對數(shù)時間的查找插入特性,為什么還要使用跳躍表?

題目解析:

面試官經(jīng)常會拿跳表和其他數(shù)據(jù)結(jié)構(gòu)進行對比,依次同時考察候選人數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)能力。

雖然二叉查找樹(Binary Search Tree)也滿足插入、查找、刪除時間復(fù)雜度為 O (logN),但是在極端情況下,如果插入的數(shù)據(jù)滿足完全有序(例如 1,2,3,4 …),則每次插入二手樹都是右節(jié)點,這時二叉查找樹會退化為線性鏈表,查詢時間復(fù)雜度退化為 O(N)。相對于二叉查找樹,跳躍表的表現(xiàn)更穩(wěn)定。

2.3 跳躍表 vs 紅黑樹

面試官提問: 紅黑樹也能實現(xiàn)對數(shù)時間的查找插入特性,為什么還要使用跳躍表?

題目解析:

紅黑樹(Red Black Tree)是二叉查找樹的一種變形,查找、插入、刪除的時間復(fù)雜度也是 O (logN),為什么 Redis 不使用紅黑樹作為 zset 的數(shù)據(jù)結(jié)構(gòu)實現(xiàn)?

關(guān)于這一點,Redis 的官方解釋已經(jīng)很全面:

There are a few reasons:

  1. They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
  2. A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
  3. They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.

總結(jié)下來就是兩點原因:

(1)相對于 B 樹 / 紅黑樹,跳躍表修改節(jié)點對內(nèi)存的變量影響??;

(2)性能接近紅黑樹,但是跳表編碼實現(xiàn)更簡單,方便調(diào)試,簡單來說就是編碼完整的紅黑樹實現(xiàn)麻煩且不直觀。

3. 小結(jié)

本章節(jié)介紹了跳躍表的基本數(shù)據(jù)結(jié)構(gòu)定義、插入查找的時間復(fù)雜度,以及和其他數(shù)據(jù)結(jié)構(gòu)的對比。跳表是常見的 Redis 底層數(shù)據(jù)結(jié)構(gòu),希望大家能夠深入理解并掌握。