精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

深入理解跳表及其在Redis中的應(yīng)用

數(shù)據(jù)庫 Redis
跳表的時間復(fù)雜度與AVL樹和紅黑樹相同,可以達(dá)到O(logN),但是AVL樹要維持高度的平衡,紅黑樹要維持高度的近似平衡,這都會導(dǎo)致插入或者刪除節(jié)點(diǎn)時的一些時間開銷,所以跳表相較于AVL樹和紅黑樹來說,省去了維持高度的平衡的時間開銷,但是相應(yīng)的也付出了更多的空間來存儲多個層的節(jié)點(diǎn),所以跳表是用空間換時間的數(shù)據(jù)結(jié)構(gòu)。

前言

跳表可以達(dá)到和紅黑樹一樣的時間復(fù)雜度 O(logN),且實(shí)現(xiàn)簡單,Redis 中的有序集合對象的底層數(shù)據(jù)結(jié)構(gòu)就使用了跳表。其作者威廉·普評價:跳躍鏈表是在很多應(yīng)用中有可能替代平衡樹的一種數(shù)據(jù)結(jié)構(gòu)。本篇文章將對跳表的實(shí)現(xiàn)及在Redis中的應(yīng)用進(jìn)行學(xué)習(xí)。

一. 跳表的基礎(chǔ)概念

跳表,即跳躍鏈表(Skip List),是基于并聯(lián)的鏈表數(shù)據(jù)結(jié)構(gòu),操作效率可以達(dá)到O(logN),對并發(fā)友好,跳表的示意圖如下所示。

跳表的特點(diǎn),可以概括如下。

?跳表是多層(level)鏈表結(jié)構(gòu);

?跳表中的每一層都是一個有序鏈表,并且按照元素升序(默認(rèn))排列;

?跳表中的元素會在哪一層出現(xiàn)是隨機(jī)決定的,但是只要元素出現(xiàn)在了第 k 層,那么 k 層以下的鏈表也會出現(xiàn)這個元素;

?跳表的底層的鏈表包含所有元素;

?跳表頭節(jié)點(diǎn)和尾節(jié)點(diǎn)不存儲元素,且頭節(jié)點(diǎn)和尾節(jié)點(diǎn)的層數(shù)就是跳表的最大層數(shù);

?跳表中的節(jié)點(diǎn)包含兩個指針,一個指針指向同層鏈表的后一節(jié)點(diǎn),一個指針指向下層鏈表的同元素節(jié)點(diǎn)。

以上圖中的跳表為例,如果要查找元素 71,那么查找流程如下圖所示。

??從頂層鏈表的頭節(jié)點(diǎn)開始查找,查找到元素71的節(jié)點(diǎn)時,一共遍歷了4個節(jié)點(diǎn),但是如果按照傳統(tǒng)鏈表的方式(即從跳表的底層鏈表的頭節(jié)點(diǎn)開始向后查找),那么就需要遍歷7個節(jié)點(diǎn),所以跳表以空間換時間,縮短了操作跳表所需要花費(fèi)的時間。跳躍列表的算法有同平衡樹一樣的漸進(jìn)的預(yù)期時間邊界,并且更簡單、更快速和使用更少的空間。這種數(shù)據(jù)結(jié)構(gòu)是由William Pugh(音譯為威廉·普)發(fā)明的,最早出現(xiàn)于他在1990年發(fā)表的論文《Skip Lists: A Probabilistic Alternative to Balanced Trees》。 谷歌上找到一篇作者關(guān)于跳表的論文,感興趣強(qiáng)烈建議下載閱讀:

?https://epaperpress.com/sortsearch/download/skiplist.pdf?

跳表在動態(tài)查找過程中使用了一種非嚴(yán)格的平衡機(jī)制來讓插入和刪除都更加便利和快捷,這種非嚴(yán)格平衡是基于概率的,而不是平衡樹的嚴(yán)格平衡。說到非嚴(yán)格平衡,首先想到的是紅黑樹RbTree,它同樣采用非嚴(yán)格平衡來避免像AVL那樣調(diào)整樹的結(jié)構(gòu),這里就不展開講紅黑樹了,看來跳表也是類似的路子,但是是基于概率實(shí)現(xiàn)的。

二. 跳表的節(jié)點(diǎn)

已知跳表中的節(jié)點(diǎn),需要有指向當(dāng)前層鏈表后一節(jié)點(diǎn)的指針,和指向下層鏈表的同元素節(jié)點(diǎn)的指針,所以跳表中的節(jié)點(diǎn),定義如下。

public class SkiplistNode {

public int data;
public SkiplistNode next;
public SkiplistNode down;
public int level;

public SkiplistNode(int data, int level) {
this.data = data;
this.level = level;
}

上述是跳表中的節(jié)點(diǎn)的最簡單的定義方式,存儲的元素 data 為整數(shù),節(jié)點(diǎn)之間進(jìn)行比較時直接比較元素 data 的大小。

三. 跳表的初始化

跳表初始化時,將每一層鏈表的頭尾節(jié)點(diǎn)創(chuàng)建出來并使用集合將頭尾節(jié)點(diǎn)進(jìn)行存儲,頭尾節(jié)點(diǎn)的層數(shù)隨機(jī)指定,且頭尾節(jié)點(diǎn)的層數(shù)就代表當(dāng)前跳表的層數(shù)。初始化后,跳表結(jié)構(gòu)如下所示。

??跳表初始化的相關(guān)代碼如下所示。

public LinkedList<SkiplistNode> headNodes;
public LinkedList<SkiplistNode> tailNodes;

public int curLevel;

public Random random;

public Skiplist() {
random = new Random();

//headNodes用于存儲每一層的頭節(jié)點(diǎn)
headNodes = new LinkedList<>();
//tailNodes用于存儲每一層的尾節(jié)點(diǎn)
tailNodes = new LinkedList<>();

//初始化跳表時,跳表的層數(shù)隨機(jī)指定
curLevel = getRandomLevel();
//指定了跳表的初始的隨機(jī)層數(shù)后,就需要將每一層的頭節(jié)點(diǎn)和尾節(jié)點(diǎn)創(chuàng)建出來并構(gòu)建好關(guān)系
SkiplistNode head = new SkiplistNode(Integer.MIN_VALUE, 0);
SkiplistNode tail = new SkiplistNode(Integer.MAX_VALUE, 0);
for (int i = 0; i <= curLevel; i++) {
head.next = tail;
headNodes.addFirst(head);
tailNodes.addFirst(tail);

SkiplistNode headNew = new SkiplistNode(Integer.MIN_VALUE, head.level + 1);
SkiplistNode tailNew = new SkiplistNode(Integer.MAX_VALUE, tail.level + 1);
headNew.down = head;
tailNew.down = tail;

head = headNew;
tail = tailNew;
}
}

四. 跳表的添加方法

每一個元素添加到跳表中時,首先需要隨機(jī)指定這個元素在跳表中的層數(shù),如果隨機(jī)指定的層數(shù)大于了跳表的層數(shù),則在將元素添加到跳表中之前,還需要擴(kuò)大跳表的層數(shù),而擴(kuò)大跳表的層數(shù)就是將頭尾節(jié)點(diǎn)的層數(shù)擴(kuò)大。下面給出需要擴(kuò)大跳表層數(shù)的一次添加的過程。

初始狀態(tài)時,跳表的層數(shù)為 2,如下圖所示。

現(xiàn)在要往跳表中添加元素 120,并且隨機(jī)指定的層數(shù)為 3,大于了當(dāng)前跳表的層數(shù) 2,此時需要先擴(kuò)大跳表的層數(shù),2如 下圖所示。

??將元素 120 插入到跳表中時,從頂層開始,逐層向下插入,如下圖所示。

??跳表的添加方法的代碼如下所示。

public void add(int num) {
//獲取本次添加的值的層數(shù)
int level = getRandomLevel();
//如果本次添加的值的層數(shù)大于當(dāng)前跳表的層數(shù)
//則需要在添加當(dāng)前值前先將跳表層數(shù)擴(kuò)充
if (level > curLevel) {
expanLevel(level - curLevel);
}

//curNode表示num值在當(dāng)前層對應(yīng)的節(jié)點(diǎn)
SkiplistNode curNode = new SkiplistNode(num, level);
//preNode表示curNode在當(dāng)前層的前一個節(jié)點(diǎn)
SkiplistNode preNode = headNodes.get(curLevel - level);
for (int i = 0; i <= level; i++) {
//從當(dāng)前層的head節(jié)點(diǎn)開始向后遍歷,直到找到一個preNode
//使得preNode.data < num <= preNode.next.data
while (preNode.next.data < num) {
preNode = preNode.next;
}

//將curNode插入到preNode和preNode.next中間
curNode.next = preNode.next;
preNode.next = curNode;

//如果當(dāng)前并不是0層,則繼續(xù)向下層添加節(jié)點(diǎn)
if (curNode.level > 0) {
SkiplistNode downNode = new SkiplistNode(num, curNode.level - 1);
//curNode指向下一層的節(jié)點(diǎn)
curNode.down = downNode;
//curNode向下移動一層
curNode = downNode;
}
//preNode向下移動一層
preNode = preNode.down;
}
}

private void expanLevel(int expanCount) {
SkiplistNode head = headNodes.getFirst();
SkiplistNode tail = tailNodes.getFirst();
for (int i = 0; i < expanCount; i++) {
SkiplistNode headNew = new SkiplistNode(Integer.MIN_VALUE, head.level + 1);
SkiplistNode tailNew = new SkiplistNode(Integer.MAX_VALUE, tail.level + 1);
headNew.down = head;
tailNew.down = tail;

head = headNew;
tail = tailNew;

headNodes.addFirst(head);
tailNodes.addFirst(tail);
}
}

五. 跳表的搜索方法

在跳表中搜索一個元素時,需要從頂層開始,逐層向下搜索。搜索時遵循如下規(guī)則。

?目標(biāo)值大于當(dāng)前節(jié)點(diǎn)的后一節(jié)點(diǎn)值時,繼續(xù)在本層鏈表上向后搜索;

?目標(biāo)值大于當(dāng)前節(jié)點(diǎn)值,小于當(dāng)前節(jié)點(diǎn)的后一節(jié)點(diǎn)值時,向下移動一層,從下層鏈表的同節(jié)點(diǎn)位置向后搜索;

?目標(biāo)值等于當(dāng)前節(jié)點(diǎn)值,搜索結(jié)束。

?下圖是一個搜索過程的示意圖。

?跳表的搜索的代碼如下所示。

public boolean search(int target) {
//從頂層開始尋找,curNode表示當(dāng)前遍歷到的節(jié)點(diǎn)
SkiplistNode curNode = headNodes.getFirst();
while (curNode != null) {
if (curNode.next.data == target) {
//找到了目標(biāo)值對應(yīng)的節(jié)點(diǎn),此時返回true
return true;
} else if (curNode.next.data > target) {
//curNode的后一節(jié)點(diǎn)值大于target
//說明目標(biāo)節(jié)點(diǎn)在curNode和curNode.next之間
//此時需要向下層尋找
curNode = curNode.down;
} else {
//curNode的后一節(jié)點(diǎn)值小于target
//說明目標(biāo)節(jié)點(diǎn)在curNode的后一節(jié)點(diǎn)的后面
//此時在本層繼續(xù)向后尋找
curNode = curNode.next;
}
}
return false;
}

六. 跳表的刪除方法

當(dāng)在跳表中需要刪除某一個元素時,則需要將這個元素在所有層的節(jié)點(diǎn)都刪除,具體的刪除規(guī)則如下所示。

?首先按照跳表的搜索的方式,搜索待刪除節(jié)點(diǎn),如果能夠搜索到,此時搜索到的待刪除節(jié)點(diǎn)位于該節(jié)點(diǎn)層數(shù)的最高層;

?從待刪除節(jié)點(diǎn)的最高層往下,將每一層的待刪除節(jié)點(diǎn)都刪除掉,刪除方式就是讓待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)直接指向待刪除節(jié)點(diǎn)的后一節(jié)點(diǎn)。

?下圖是一個刪除過程的示意圖。

?跳表的刪除的代碼如下所示。

public boolean erase(int num) {
//刪除節(jié)點(diǎn)的遍歷過程與尋找節(jié)點(diǎn)的遍歷過程是相同的
//不過在刪除節(jié)點(diǎn)時如果找到目標(biāo)節(jié)點(diǎn),則需要執(zhí)行節(jié)點(diǎn)刪除的操作
SkiplistNode curNode = headNodes.getFirst();
while (curNode != null) {
if (curNode.next.data == num) {
//preDeleteNode表示待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)
SkiplistNode preDeleteNode = curNode;
while (true) {
//刪除當(dāng)前層的待刪除節(jié)點(diǎn),就是讓待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)指向待刪除節(jié)點(diǎn)的后一節(jié)點(diǎn)
preDeleteNode.next = curNode.next.next;
//當(dāng)前層刪除完后,需要繼續(xù)刪除下一層的待刪除節(jié)點(diǎn)
//這里讓preDeleteNode向下移動一層
//向下移動一層后,preDeleteNode就不一定是待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)了
preDeleteNode = preDeleteNode.down;

//如果preDeleteNode為null,說明已經(jīng)將底層的待刪除節(jié)點(diǎn)刪除了
//此時就結(jié)束刪除流程,并返回true
if (preDeleteNode == null) {
return true;
}

//preDeleteNode向下移動一層后,需要繼續(xù)從當(dāng)前位置向后遍歷
//直到找到一個preDeleteNode,使得preDeleteNode.next的值等于目標(biāo)值
//此時preDeleteNode就又變成了待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)
while (preDeleteNode.next.data != num) {
preDeleteNode = preDeleteNode.next;
}
}
} else if (curNode.next.data > num) {
curNode = curNode.down;
} else {
curNode = curNode.next;
}
}
return false;
}

七. 跳表完整代碼

跳表完整代碼如下所示。

public class Skiplist {

public LinkedList<SkiplistNode> headNodes;
public LinkedList<SkiplistNode> tailNodes;

public int curLevel;

public Random random;

public Skiplist() {
random = new Random();

//headNodes用于存儲每一層的頭節(jié)點(diǎn)
headNodes = new LinkedList<>();
//tailNodes用于存儲每一層的尾節(jié)點(diǎn)
tailNodes = new LinkedList<>();

//初始化跳表時,跳表的層數(shù)隨機(jī)指定
curLevel = getRandomLevel();
//指定了跳表的初始的隨機(jī)層數(shù)后,就需要將每一層的頭節(jié)點(diǎn)和尾節(jié)點(diǎn)創(chuàng)建出來并構(gòu)建好關(guān)系
SkiplistNode head = new SkiplistNode(Integer.MIN_VALUE, 0);
SkiplistNode tail = new SkiplistNode(Integer.MAX_VALUE, 0);
for (int i = 0; i <= curLevel; i++) {
head.next = tail;
headNodes.addFirst(head);
tailNodes.addFirst(tail);

SkiplistNode headNew = new SkiplistNode(Integer.MIN_VALUE, head.level + 1);
SkiplistNode tailNew = new SkiplistNode(Integer.MAX_VALUE, tail.level + 1);
headNew.down = head;
tailNew.down = tail;

head = headNew;
tail = tailNew;
}
}

public boolean search(int target) {
//從頂層開始尋找,curNode表示當(dāng)前遍歷到的節(jié)點(diǎn)
SkiplistNode curNode = headNodes.getFirst();
while (curNode != null) {
if (curNode.next.data == target) {
//找到了目標(biāo)值對應(yīng)的節(jié)點(diǎn),此時返回true
return true;
} else if (curNode.next.data > target) {
//curNode的后一節(jié)點(diǎn)值大于target
//說明目標(biāo)節(jié)點(diǎn)在curNode和curNode.next之間
//此時需要向下層尋找
curNode = curNode.down;
} else {
//curNode的后一節(jié)點(diǎn)值小于target
//說明目標(biāo)節(jié)點(diǎn)在curNode的后一節(jié)點(diǎn)的后面
//此時在本層繼續(xù)向后尋找
curNode = curNode.next;
}
}
return false;
}

public void add(int num) {
//獲取本次添加的值的層數(shù)
int level = getRandomLevel();
//如果本次添加的值的層數(shù)大于當(dāng)前跳表的層數(shù)
//則需要在添加當(dāng)前值前先將跳表層數(shù)擴(kuò)充
if (level > curLevel) {
expanLevel(level - curLevel);
}

//curNode表示num值在當(dāng)前層對應(yīng)的節(jié)點(diǎn)
SkiplistNode curNode = new SkiplistNode(num, level);
//preNode表示curNode在當(dāng)前層的前一個節(jié)點(diǎn)
SkiplistNode preNode = headNodes.get(curLevel - level);
for (int i = 0; i <= level; i++) {
//從當(dāng)前層的head節(jié)點(diǎn)開始向后遍歷,直到找到一個preNode
//使得preNode.data < num <= preNode.next.data
while (preNode.next.data < num) {
preNode = preNode.next;
}

//將curNode插入到preNode和preNode.next中間
curNode.next = preNode.next;
preNode.next = curNode;

//如果當(dāng)前并不是0層,則繼續(xù)向下層添加節(jié)點(diǎn)
if (curNode.level > 0) {
SkiplistNode downNode = new SkiplistNode(num, curNode.level - 1);
//curNode指向下一層的節(jié)點(diǎn)
curNode.down = downNode;
//curNode向下移動一層
curNode = downNode;
}
//preNode向下移動一層
preNode = preNode.down;
}
}

public boolean erase(int num) {
//刪除節(jié)點(diǎn)的遍歷過程與尋找節(jié)點(diǎn)的遍歷過程是相同的
//不過在刪除節(jié)點(diǎn)時如果找到目標(biāo)節(jié)點(diǎn),則需要執(zhí)行節(jié)點(diǎn)刪除的操作
SkiplistNode curNode = headNodes.getFirst();
while (curNode != null) {
if (curNode.next.data == num) {
//preDeleteNode表示待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)
SkiplistNode preDeleteNode = curNode;
while (true) {
//刪除當(dāng)前層的待刪除節(jié)點(diǎn),就是讓待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)指向待刪除節(jié)點(diǎn)的后一節(jié)點(diǎn)
preDeleteNode.next = curNode.next.next;
//當(dāng)前層刪除完后,需要繼續(xù)刪除下一層的待刪除節(jié)點(diǎn)
//這里讓preDeleteNode向下移動一層
//向下移動一層后,preDeleteNode就不一定是待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)了
preDeleteNode = preDeleteNode.down;

//如果preDeleteNode為null,說明已經(jīng)將底層的待刪除節(jié)點(diǎn)刪除了
//此時就結(jié)束刪除流程,并返回true
if (preDeleteNode == null) {
return true;
}

//preDeleteNode向下移動一層后,需要繼續(xù)從當(dāng)前位置向后遍歷
//直到找到一個preDeleteNode,使得preDeleteNode.next的值等于目標(biāo)值
//此時preDeleteNode就又變成了待刪除節(jié)點(diǎn)的前一節(jié)點(diǎn)
while (preDeleteNode.next.data != num) {
preDeleteNode = preDeleteNode.next;
}
}
} else if (curNode.next.data > num) {
curNode = curNode.down;
} else {
curNode = curNode.next;
}
}
return false;
}

private void expanLevel(int expanCount) {
SkiplistNode head = headNodes.getFirst();
SkiplistNode tail = tailNodes.getFirst();
for (int i = 0; i < expanCount; i++) {
SkiplistNode headNew = new SkiplistNode(Integer.MIN_VALUE, head.level + 1);
SkiplistNode tailNew = new SkiplistNode(Integer.MAX_VALUE, tail.level + 1);
headNew.down = head;
tailNew.down = tail;

head = headNew;
tail = tailNew;

headNodes.addFirst(head);
tailNodes.addFirst(tail);
}
}

private int getRandomLevel() {
int level = 0;
while (random.nextInt(2) > 1) {
level++;
}
return level;
}

}

八. 跳表在Redis中的應(yīng)用

ZSet結(jié)構(gòu)同時包含一個字典和一個跳躍表,跳躍表按score從小到大保存所有集合元素。字典保存著從member到score的映射。這兩種結(jié)構(gòu)通過指針共享相同元素的member和score,不會浪費(fèi)額外內(nèi)存。

typedef struct zset {
dict *dict;
zskiplist *zsl;
} zset;

ZSet中的字典和跳表布局:

1.ZSet中跳表的實(shí)現(xiàn)細(xì)節(jié)

隨機(jī)層數(shù)的實(shí)現(xiàn)原理:

跳表是一個概率型的數(shù)據(jù)結(jié)構(gòu),元素的插入層數(shù)是隨機(jī)指定的。Willam Pugh在論文中描述了它的計(jì)算過程如下:指定節(jié)點(diǎn)最大層數(shù) MaxLevel,指定概率 p, 默認(rèn)層數(shù) lvl 為1;

生成一個0~1的隨機(jī)數(shù)r,若r<p,且lvl<MaxLevel ,則lvl ++;

重復(fù)第 2 步,直至生成的r >p 為止,此時的 lvl 就是要插入的層數(shù)。

論文中生成隨機(jī)層數(shù)的偽碼:

在Redis中對跳表的實(shí)現(xiàn)基本上也是遵循這個思想的,只不過有微小差異,看下Redis關(guān)于跳表層數(shù)的隨機(jī)源碼src/z_set.c:

/* Returns a random level for the new skiplist node we are going to create.
* The return value of this function is between 1 and ZSKIPLIST_MAXLEVEL
* (both inclusive), with a powerlaw-alike distribution where higher
* levels are less likely to be returned. */
int zslRandomLevel(void) {
int level = 1;
while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF))
level += 1;
return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;
}

其中兩個宏的定義在redis.h中:

#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */

可以看到while中的:

(random()&0xFFFF) < (ZSKIPLIST_P*0xFFFF)

第一眼看到這個公式,因?yàn)樯婕拔贿\(yùn)算有些詫異,需要研究一下Antirez為什么使用位運(yùn)算來這么寫?

最開始的猜測是random()返回的是浮點(diǎn)數(shù)[0-1],于是乎在線找了個浮點(diǎn)數(shù)轉(zhuǎn)二進(jìn)制的工具,輸入0.5看了下結(jié)果:

可以看到0.5的32bit轉(zhuǎn)換16進(jìn)制結(jié)果為0x3f000000,如果與0xFFFF做與運(yùn)算結(jié)果還是0,不符合預(yù)期。

實(shí)際應(yīng)用時對于隨機(jī)層數(shù)的實(shí)現(xiàn)并不統(tǒng)一,重要的是隨機(jī)數(shù)的生成,在LevelDB中對跳表層數(shù)的生成代碼是這樣的:

template <typename Key, typename Value>
int SkipList<Key, Value>::randomLevel() {

static const unsigned int kBranching = 4;
int height = 1;
while (height < kMaxLevel && ((::Next(rnd_) % kBranching) == 0)) {
height++;
}
assert(height > 0);
assert(height <= kMaxLevel);
return height;
}

uint32_t Next( uint32_t& seed) {
seed = seed & 0x7fffffffu;

if (seed == 0 || seed == 2147483647L) {
seed = 1;
}
static const uint32_t M = 2147483647L;
static const uint64_t A = 16807;
uint64_t product = seed * A;
seed = static_cast<uint32_t>((product >> 31) + (product & M));
if (seed > M) {
seed -= M;
}
return seed;
}

可以看到leveldb使用隨機(jī)數(shù)與kBranching取模,如果值為0就增加一層,這樣雖然沒有使用浮點(diǎn)數(shù),但是也實(shí)現(xiàn)了概率平衡。

2.跳表結(jié)點(diǎn)的平均層數(shù)

我們很容易看出,產(chǎn)生越高的節(jié)點(diǎn)層數(shù)出現(xiàn)概率越低,無論如何層數(shù)總是滿足冪次定律越大的數(shù)出現(xiàn)的概率越小。

如果某件事的發(fā)生頻率和它的某個屬性成冪關(guān)系,那么這個頻率就可以稱之為符合冪次定律。

冪次定律的表現(xiàn)是少數(shù)幾個事件的發(fā)生頻率占了整個發(fā)生頻率的大部分, 而其余的大多數(shù)事件只占整個發(fā)生頻率的一個小部分。

冪次定律應(yīng)用到跳表的隨機(jī)層數(shù)來說就是大部分的節(jié)點(diǎn)層數(shù)都是黃色部分,只有少數(shù)是綠色部分,并且概率很低。

定量的分析如下:

?節(jié)點(diǎn)層數(shù)至少為1,大于1的節(jié)點(diǎn)層數(shù)滿足一個概率分布。

?節(jié)點(diǎn)層數(shù)恰好等于1的概率為p^0(1-p)

?節(jié)點(diǎn)層數(shù)恰好等于2的概率為p^1(1-p)

?節(jié)點(diǎn)層數(shù)恰好等于3的概率為p^2(1-p)

?節(jié)點(diǎn)層數(shù)恰好等于4的概率為p^3(1-p)

依次遞推節(jié)點(diǎn)層數(shù)恰好等于K的概率為p^(k-1)(1-p)

因此如果我們要求節(jié)點(diǎn)的平均層數(shù),那么也就轉(zhuǎn)換成了求概率分布的期望問題了:

??表中P為概率,V為對應(yīng)取值,給出了所有取值和概率的可能,因此就可以求這個概率分布的期望了。方括號里面的式子其實(shí)就是高一年級學(xué)的等比數(shù)列,常用技巧錯位相減求和,從中可以看到結(jié)點(diǎn)層數(shù)的期望值與1-p成反比。對于Redis而言,當(dāng)p=0.25時結(jié)點(diǎn)層數(shù)的期望是1.33。

總結(jié)

跳表的時間復(fù)雜度與AVL樹和紅黑樹相同,可以達(dá)到O(logN),但是AVL樹要維持高度的平衡,紅黑樹要維持高度的近似平衡,這都會導(dǎo)致插入或者刪除節(jié)點(diǎn)時的一些時間開銷,所以跳表相較于AVL樹和紅黑樹來說,省去了維持高度的平衡的時間開銷,但是相應(yīng)的也付出了更多的空間來存儲多個層的節(jié)點(diǎn),所以跳表是用空間換時間的數(shù)據(jù)結(jié)構(gòu)。以Redis中底層的數(shù)據(jù)結(jié)構(gòu)zset作為典型應(yīng)用來展開,進(jìn)一步看到跳躍鏈表的實(shí)際應(yīng)用。

責(zé)任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2021-06-30 17:55:34

Redis應(yīng)用跳表

2022-02-14 07:47:26

overlayfsdockerrootfs

2010-07-26 11:27:58

Perl閉包

2020-09-23 10:00:26

Redis數(shù)據(jù)庫命令

2024-05-11 07:13:33

C#Task編程

2022-11-07 18:12:54

Go語言函數(shù)

2024-10-11 11:54:14

C#編寫異步

2024-04-24 08:32:55

.NET對象映射

2024-07-18 10:12:04

2023-12-31 12:56:02

C++內(nèi)存編程

2020-12-16 09:47:01

JavaScript箭頭函數(shù)開發(fā)

2018-07-09 15:11:14

Java逃逸JVM

2016-08-31 15:50:50

PythonThreadLocal變量

2010-06-28 10:12:01

PHP匿名函數(shù)

2023-10-08 08:53:36

數(shù)據(jù)庫MySQL算法

2014-06-23 10:42:56

iOS開發(fā)UIScrollVie

2016-12-08 15:36:59

HashMap數(shù)據(jù)結(jié)構(gòu)hash函數(shù)

2020-07-21 08:26:08

SpringSecurity過濾器

2010-06-01 15:25:27

JavaCLASSPATH

2013-11-05 13:29:04

JavaScriptreplace
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

97久久超碰精品国产| 精品久久一区| 亚洲欧美日韩综合aⅴ视频| 91青青草免费观看| 久久久一区二区三区四区| 成人性生交大片免费看中文视频| 天天色天天操综合| 热re99久久精品国99热蜜月| 亚洲系列在线观看| 国产专区一区| 中文字幕日韩欧美| 欧洲成人午夜精品无码区久久| 成人三级高清视频在线看| 欧美经典一区二区三区| av一区二区三区免费| 69国产精品视频免费观看| 亚洲高清影视| 亚洲欧洲黄色网| 免费黄视频在线观看| 在线看欧美视频| 一区二区三区欧美日韩| 日韩一区二区三区高清| 亚洲精品无码专区| 麻豆视频一区二区| 欧美在线视频一区| 欧美成人手机视频| 日韩欧美精品综合| 日韩电影视频免费| 日本肉体xxxx裸体xxx免费| 国产福利在线免费观看| 国产精品美女视频| 另类视频在线观看+1080p| 国内精品久久久久久久久久| 蜜臀精品久久久久久蜜臀| 91av视频在线观看| 久久综合色综合| 亚洲精品97| 中文字幕国产精品久久| 久久精品国产亚洲av麻豆| 日韩视频1区| 制服丝袜中文字幕亚洲| 天天爽天天爽夜夜爽| 欧亚在线中文字幕免费| 亚洲一区二区三区四区五区中文 | 精品国产va久久久久久久| 老鸭窝91久久精品色噜噜导演| 久久久午夜视频| 九九热精品免费视频| 99精品在线免费在线观看| 国产一区二区动漫| 无码少妇精品一区二区免费动态| 网曝91综合精品门事件在线| 亚洲精品av在线| 日韩成人av影院| 午夜视频在线观看精品中文| 日韩亚洲欧美综合| 中文字幕第六页| 视频一区日韩精品| 日韩欧美的一区二区| 丰满少妇一区二区三区专区| 日本免费一区二区视频| 日韩欧美在线影院| 催眠调教后宫乱淫校园| 精品午夜电影| 亚洲精品一区久久久久久| 大地资源二中文在线影视观看| 丝袜美腿综合| 亚洲人永久免费| 中文字幕精品亚洲| 91精品国产成人观看| 久久伊人色综合| 免费在线观看一级片| 欧美日韩一视频区二区| 高清欧美性猛交xxxx黑人猛交| 日韩精品久久久久久久| 久久成人精品| 国产美女直播视频一区| 国产精品一级二级| 成人三级在线视频| 蜜桃视频在线观看91| 国产一二三区在线视频| 18成人在线观看| 无码熟妇人妻av在线电影| 国产精品蜜芽在线观看| 在线精品视频一区二区三四| 欧美丝袜在线观看| 国产精品45p| 亚洲色图18p| 91精品少妇一区二区三区蜜桃臀| 欧美激情1区| 欧美亚洲成人免费| 亚洲网站在线免费观看| 国产91对白在线观看九色| 另类视频在线观看+1080p| 秋霞成人影院| 精品国产91久久久久久| www.com黄色片| 91精品国产自产在线丝袜啪 | av片在线观看网站| 五月激情丁香一区二区三区| 色婷婷综合网站| 久久91在线| 精品国产一区二区三区四区在线观看| 久久久久久久福利| 日本一不卡视频| 99蜜桃在线观看免费视频网站| 日中文字幕在线| 亚洲免费在线播放| 热久久精品免费视频| 欧美成人精品一级| 亚洲最新av在线网站| 日本少妇xxxx动漫| 蜜桃av噜噜一区二区三区小说| 国产日本一区二区三区| 麻豆视频在线观看免费网站| 黑人精品xxx一区| 中文字幕av一区二区三区人妻少妇 | 麻豆传媒在线免费看| 欧美日韩另类字幕中文| 午夜免费一级片| av资源久久| 欧美中文字幕在线观看| 午夜精品一区二区三| 国产精品久久久久一区二区三区| 波多野结衣乳巨码无在线| 日韩一区二区三区精品| 日韩在线观看免费全集电视剧网站| 亚洲黄色一区二区| 国产69精品久久久久777| 亚洲最新在线| julia一区二区三区中文字幕| 亚洲国产欧美久久| 国产精品久久久久久久精| 另类欧美日韩国产在线| 欧美一区三区二区在线观看| 女海盗2成人h版中文字幕| 日韩欧美中文字幕制服| 一级片一级片一级片| 美日韩一区二区三区| 日韩电影免费观看在| 欧美片第1页| 国产偷亚洲偷欧美偷精品| 日韩欧美一区二区一幕| 岛国一区二区在线观看| 日产精品久久久久久久蜜臀| 国产一区二区三区视频在线| yellow中文字幕久久| 91精东传媒理伦片在线观看| 中国色在线观看另类| 成人免费毛片播放| 精品久久久久久久久久久aⅴ| 国产精品99一区| 国产一区二区三区福利| 欧美吞精做爰啪啪高潮| 亚洲精品国产精品国自| 蜜桃视频在线观看一区二区| 亚洲人成网站在线观看播放| 黑人一区二区三区| 久久精品亚洲热| 国产99999| 亚洲观看高清完整版在线观看| 亚洲熟女一区二区三区| 亚洲久久一区| 欧洲亚洲一区二区| 国产极品久久久久久久久波多结野| 国产亚洲精品久久久| 亚洲视频久久久| 亚洲女厕所小便bbb| 久久国产免费视频| 香蕉久久夜色精品国产| 视频在线观看成人| 亚洲欧洲一二区| 欧美国产第一页| 四虎电影院在线观看| 日韩欧美国产视频| 久久精品国产亚洲AV成人婷婷| 国产毛片精品视频| 九一免费在线观看| 日韩有码中文字幕在线| 国产精品美乳一区二区免费 | 国产不卡精品视男人的天堂| eeuss影院www在线播放| 7777精品伊人久久久大香线蕉超级流畅 | 佐佐木明希电影| 99精品视频免费观看视频| 日韩一区国产在线观看| 亚洲1区在线| 日本亚洲精品在线观看| 香港伦理在线| 亚洲第一免费播放区| 欧美性猛交xxxx乱大交hd | 国产福利久久久| 久久久777精品电影网影网 | 久久久久亚洲蜜桃| www.国产福利| 在线观看一区视频| 亚洲欧洲精品在线观看| 午夜久久av| 国产精品美女网站| 极品av在线| 中文字幕亚洲精品| 天堂av中文在线资源库| 91精品国产综合久久香蕉麻豆| 青青国产在线观看| 亚洲视频1区2区| 免费人成又黄又爽又色| 岛国一区二区三区| 免费成人黄色大片| 久久国产日本精品| 久久久国内精品| 久久中文视频| 日本a级片久久久| 成人高潮视频| 91亚洲精品在线| 日韩欧美精品电影| 97国产精品免费视频| av在线免费网站| 在线精品91av| 国产一级片在线| 亚洲精品国产美女| 亚洲AV无码一区二区三区性| 欧美日韩综合不卡| 欧美男人亚洲天堂| 大桥未久av一区二区三区| 中文字幕手机在线观看| 亚洲欧洲性图库| 成人无码精品1区2区3区免费看 | 国产四区在线观看| 精品欧美久久| 久久精品第九区免费观看| 一本色道69色精品综合久久| 91精品在线观| 日韩午夜视频在线| 国产九九精品视频| yiren22亚洲综合| 国产成人精品在线视频| yellow在线观看网址| 欧美国产在线视频| 污的网站在线观看| 欧美国产日本高清在线| 青草视频在线免费直播| 欧美成人四级hd版| 亚洲婷婷噜噜| 欧美裸体xxxx极品少妇| 怡红院av在线| 欧美大片免费观看在线观看网站推荐| 国产调教视频在线观看| 超碰精品一区二区三区乱码| 拍真实国产伦偷精品| 日韩在线观看av| 精品国产丝袜高跟鞋| xxx欧美精品| 成人在线观看亚洲| 欧美日韩xxx| 91制片在线观看| 91精品国产99| 欧美色网一区| 国产精品视频一区国模私拍| 久久亚洲资源中文字| 成人精品视频久久久久| 精品成人18| 国产成人亚洲欧美| 天天久久夜夜| 视频一区二区在线观看| 天天插综合网| 国产免费裸体视频| 日韩亚洲在线| 美女网站视频黄色| 国产乱人伦偷精品视频免下载 | 国产女无套免费视频| 日韩一二三区不卡| 午夜黄色小视频| 国产一级揄自揄精品视频| 黄视频网站在线| 欧美寡妇偷汉性猛交| 一区二区三区短视频| 国产日韩欧美91| 2021年精品国产福利在线| 蜜桃麻豆91| 亚洲女同一区| 成熟丰满熟妇高潮xxxxx视频| 石原莉奈一区二区三区在线观看 | 97色婷婷成人综合在线观看| av在线亚洲男人的天堂| 你懂的一区二区三区| 亚洲一卡二卡三卡| 亚洲电影av| 日本在线观看免费视频| 国产成人精品免费看| 日韩在线免费观看av| 亚洲天堂av老司机| 国内精品福利视频| 欧美一区二区三区影视| 三级视频网站在线| 久久精品视频免费播放| 亚洲天堂av在线| 亚洲字幕一区二区| 国产成人黄色| 国产成a人亚洲精v品在线观看| 久久精品一区二区三区中文字幕| 中国黄色片一级| 久久综合久久综合九色| 亚洲天堂一级片| 色中色一区二区| 亚洲精品视频91| 久久精品国产96久久久香蕉| 五月天av在线| 99久久精品久久久久久ai换脸| 精品不卡一区| 97国产在线播放| 国产风韵犹存在线视精品| 91精品国自产在线| 岛国av一区二区三区| 亚洲第一第二区| 俺去了亚洲欧美日韩| 国精产品一区一区三区四川| 国外成人在线视频网站| 亚洲成av人片一区二区密柚| 污版视频在线观看| 久久天天做天天爱综合色| 国产在线拍揄自揄拍| 4hu四虎永久在线影院成人| 久草在线青青草| 538国产精品视频一区二区| 第四色在线一区二区| 日本xxx免费| 国产专区综合网| 久久噜噜色综合一区二区| 在线观看免费一区| 久草在线青青草| 欧美一级视频免费在线观看| 精品深夜福利视频| 野外做受又硬又粗又大视频√| 国产一区二区三区免费播放| 欧美性生交大片| 欧美人妇做爰xxxⅹ性高电影 | 亚洲乱色熟女一区二区三区| 久久精品色欧美aⅴ一区二区| 欧美xxxx性| 伊人久久大香线蕉精品| 免费成人在线观看| 怡红院一区二区三区| 91黄色激情网站| 高清日韩av电影| 国产精品久久久久av| av一区二区在线播放| 日本肉体xxxx裸体xxx免费| 中文字幕制服丝袜一区二区三区| 一本色道久久综合亚洲| 中文字幕在线成人| 电影一区中文字幕| 久久最新免费视频| 国产精品一区二区不卡| 老女人性淫交视频| 日韩精品一区二区三区在线| 好吊日av在线| 久久精品美女| 久久亚洲电影| 精品女人久久久| 欧美一级片在线看| bl在线肉h视频大尺度| 九九久久99| 日本va欧美va欧美va精品| 欧美a级片免费看| 日韩精品一区二区三区四区视频| 国产精品探花在线| 日本一区二区高清视频| 精油按摩中文字幕久久| 印度午夜性春猛xxx交| 亚洲国产精品美女| 欧美日韩免费看片| 麻豆md0077饥渴少妇| 国产丶欧美丶日本不卡视频| 天海翼一区二区| 亚洲午夜女主播在线直播| 国产精品视频一区二区三区综合| 久久人人爽人人爽人人av| 26uuu久久天堂性欧美| 在线观看免费高清视频| 欧美激情xxxxx| 狠狠做六月爱婷婷综合aⅴ| 在线黄色免费看| 亚瑟在线精品视频| 最新97超碰在线| 国产日韩欧美精品| 免费xxxx性欧美18vr| 久久精品视频久久| 亚洲一级片在线看| 亚洲一区二区三区免费| 日本中文字幕高清| 亚洲国产综合91精品麻豆| 大地资源中文在线观看免费版| 99超碰麻豆| 日本欧美韩国一区三区| 精品无码m3u8在线观看| 最新中文字幕亚洲| 四虎影视精品| 亚洲欧美日韩网站| 在线观看视频91| 春色校园综合激情亚洲|