一个基于运气的数据结构,你猜是啥? (中)

简介: 一个基于运气的数据结构,你猜是啥? (中)

那么这个时候有个小问题就来了:怎么从 21 直接到 25 的呢?

看论文中的图片,稍微有一点不容易明白。

所以,我给大家重新画个示意图:

image.png

在 21 到 26 节点之间,往下了一层,逻辑也很简单。

21 节点有一个右指针指向 26,先判断右指针的值大于查询的值了。

于是下指针就起到作用了,往下一层,再继续进行右指针的判断。

其实每个节点的判断逻辑都是这样,只是前面的判断结果是进行走右指针。

按照这个往上抽节点的思想,假设我们抽到第四层,也就是论文中的这个示意图:


image.png


我们查询 25 的时候,只需要经过 2 次。

第一步就直接跳过了 21 之前的所有元素。

怎么样,爽不爽?


image.png


但是,它是有缺陷的。

火男的论文里面是这样说的


image.png


This data structure could be used for fast searching, but insertion and deletion would be impractical.

查询确实飞快。但是对于插入和删除 would be impractical。

impractical 是什么意思?



image.png


你看,又学一个四级单词。


image.png


对于插入和删除几乎是难以实现的。

你想啊,上面那个最底层的有序链表,我一开始就拿出来给你了。

然后我就说基于这个有序链表每隔一个节点抽离到上一层去,再构建一个链表。那么这样上下层节点比例应该是 2:1。巴拉巴拉的.....

但是实际情况应该是我们最开始的时候连这个有序链表都没有,需要自己去创建的。

就假设要在现有的这个跳表结构中插入一个节点,毋庸置疑,肯定是要插入到最底层的有序链表中的。

但是你破坏了上下层 1:2 的比例了呀?

怎么办,一层层的调整呗。

可以,但是请你考虑一下编码实现起来的难度和对应的时间复杂度?

要这样搞,直接就是一波劝退。

这就受不了了?

我还没说删除的事呢。

那怎么办?

看看论文里面怎么说到:


image.png


首先我们关注一下第一段划红线的地方。

火男写到:50% 的节点在第一层,25% 的节点在第二层, 12.5% 的节点在第三层。

你以为他在给你说什么?

他要表达的意思除了每一层节点的个数之外,还说明了层级:


image.png

没有第 0 层,至少论文里面没有说有第 0 层。

如果你非要说最下面那个有全部节点的有序链表叫做第 0 层,我觉得也可以。但是,我觉得叫它基础链表更加合适一点。

然后我再看第二段划线的地方。

火男提到了一个关键词:randomly,意思是随机。

说出来你可能不信,但是跳表是用随机的方式解决上面提出的插入(删除)之后调整结构的问题。

怎么随机呢?抛硬币。

是的,没有骗你,真的是“抛硬币”。


image.png


跳表中的“硬币”


当跳表中插入一个元素的时候,火男表示我们上下层之间可以不严格遵循 1:2 的节点关系。

如果插入的这个元素需要建立索引,那么把索引建立在第几层,都是由抛硬币决定的。

或者说:由抛硬币的概率决定的。

我问你,一个硬币抛出去之后,是正面的概率有多大?

是不是 50%?

如果我们把这个概率记为 p,那么 50%,即 p=1/2。

上面我们提到的概率,到底是怎么用的呢?

火男的论文中有一小节是这样的写的:


image.png


非常关键的一张图啊。

短短几行代码,描述的是如何选择层级的随机算法。

首先定义初始层级为 1(lvl := 1)。

然后有一行注释:random() that returns a random value in [0...1)

random() 返回一个 [0...1) 之间的随机值。

接下来一个 while...do 循环。

循环条件两个。

第一个:random() < p。由于 p = 1/2,那么该条件成立的概率也是 1/2。

如果每随机一次,满足 random() < p,那么层级加一。

那假设你运气爆棚,接连一百次随机出来的数都是小于 p 的怎么办?岂不是层级也到 100 层了?

第二个条件 lvl < MaxLevel,就是防止这种情况的。可以保证算出来的层级不会超过指定的 MaxLevel。

这样看来,虽然每次都是基于概率决定在那个层级,但是总体趋势是趋近于 1/2 的。

带来的好处是,每次插入都是独立的,只需要调整插入前后节点的指针即可。

一次插入就是一次查询加更新的操作,比如下面的这个示意图:


image.png

另外对于这个概率,其实火男在论文专门写了一个小标题,还给出了一个图表:

image.png

目录
相关文章
|
5月前
|
算法 搜索推荐 C++
【数据结构】手撕排序(二)
【数据结构】手撕排序(二)
|
10月前
|
存储 算法
【手撕数据结构】(一)时间复杂度
【手撕数据结构】(一)时间复杂度
81 0
|
5月前
|
存储 搜索推荐 算法
数据结构奇妙旅程之七大排序
数据结构奇妙旅程之七大排序
|
5月前
【手撕数据结构】二分查找(好多细节)
【手撕数据结构】二分查找(好多细节)
|
5月前
|
存储 算法 NoSQL
刻意练习:数据结构复习思路
刻意练习:数据结构复习思路
|
5月前
|
搜索推荐 算法
【数据结构】手撕排序(一)
【数据结构】手撕排序
|
10月前
|
存储 算法
【手撕数据结构】(二)空间复杂度
【手撕数据结构】(二)空间复杂度
82 0
|
人工智能 搜索推荐 算法
【数据结构】手撕八大排序算法(上)
目录 1.排序的概念: 2.八大排序的思路及其细节 2.1直接插入排序
53 0
|
搜索推荐
【数据结构】手撕八大排序算法(下)
2.6快速排序: 快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,
70 0
|
机器学习/深度学习 算法
数据结构刷题:第十天
时间复杂度:O(n),其中 nn 为二叉搜索树的节点个数。Morris 遍历中每个节点会被访问两次,因此总时间复杂度为 O(2n)=O(n)。
46 0
数据结构刷题:第十天