数据结构和算法—跳表_3

跳表是一种用于提高链表查询效率的数据结构,通过建立多级索引来加速查找过程。其时间复杂度为O(logn),空间复杂度为O(n)。在Redis的有序集合中,跳表被用来实现高效查找。插入和删除操作在跳表中也具有O(logn)的时间复杂度,并且可以通过随机函数决定节点插入的索引层数,以平衡时间和空间成本。

一、跳表的概述

一个值有序的链表建立多级索引,比如每 2 个节点提取一个节点到上一级,我们把抽出来的那一级叫做索引或索引层。如下图所示,其中 down 表示 down 指针,指向下一级节点。以此类推,对于节点数为 n 的链表,大约可以建立 log2n-1 级索引。像这种为链表建立多级索引的数据结构就称为跳表。(并且Redis中的有序集合(Sorted Set)就是用跳表来实现的)
在这里插入图片描述
那怎么来提高查找效率呢?如果像图中那样,对链表建立一级 “ 索引 ” ,查找起来是不是就会更快一些呢?每两个结点提取一个结点到上一级,我们把抽出来的那一级叫作索引或索引层。你可以看我画的图。图中的down表示down指针,指向下一级结点。
在这里插入图片描述
如果我们现在要查找某个结点,比如 16 。我们可以先在索引层遍历,当遍历到索引层中值为 13 的结点时,我们发现下一个结点是 17 ,那要查找的结点 16 肯定就在这两个结点之间。然后我们通过索引层结点的 down 指针,下降到原始链表这一层,继续遍历。这个时候,我们只需要再遍历 2 个结点,就可以找到值等于 16 的这个结点了。这样,原来如果要查找 16 ,需要遍历 10 个结点,现在只需要遍历 7 个结点。
从这个例子里,我们看出,加来一层索引之后,查找一个结点需要遍历的结点个数减少了,也就是说查找效率提高了。那如果我们再加一级索引呢?效率会不会提升更多呢?
在这里插入图片描述
我举的例子数据量不大,所以即便加了两级索引,查找效率的提升也并不明显。为了让你能真切地感受索引提升查询效率。我画了一个包含 64 个结点的链表,按照前面讲的这种思路,建立了五级索引。
在这里插入图片描述
从图中我们可以看出,原来没有索引的时候,查找 62 需要遍历 62 个结点,现在只需要遍历 11 个结点,速度是不是提高了很多?所以,当链表的长度 n 比较大时,比如 1000 、 10000 的时候,在构建索引之后,查找效率的提升就会非常明显。
前面讲的这种链表加多级索引的结构,就是跳表。我通过例子给你展示了跳表是如何减少查询次数的,现在你应该比较清晰地知道,跳表确实是可以提高查询效率的。接下来,我会定量地分析一下,用跳表查询到底有多快。

二、跳表的时间复杂度?

2.1 计算跳表的高度

如果链表有 n 个节点,每 2 个节点抽取抽出一个节点作为上一级索引的节点,那第 1 级索引的节点个数大约是 n/2 ,第 2 级索引的节点个数大约是 n/4 ,依次类推,第 k 级索引的节点个数就是 n/(2^k) 。 假设索引有 h 级别,最高级的索引有 2 个节点,则有 n/(2^h)=2 ,得出 h=log2n-1 ,包含原始链表这一层,整个跳表的高度就是 log2n 。
在这里插入图片描述

2.2 计算跳表的时间复杂度

假设我们在跳表中查询某个数据的时候,如果每一层都遍历 m 个节点,那在跳表中查询一个数据的时间复杂度就是 O(m*logn) 。那这个 m 是多少呢?如下图所示,假设我们要查找的数据是 x ,在第 k 级索引中,我们遍历到 y 节点之后,发现 x 大于 y ,小于后面的节点 z ,所以我们通过 y 的 down 指针,从第 k 级下降到第 k-1 级索引。在第 k-1 级索引中, y 和 z 之间只有 3 个节点(包含 y 和 z ),所以,我们在 k-1 级索引中最多只需要遍历 3 个节点,以此类推,每一级索引都最多只需要遍历 3 个节点。所以 m=3 。因此在跳表中查询某个数据的时间复杂度就是 O(logn) 。

三、跳表的空间复杂度及如何优化?

3.1 计算索引的节点总数

如果链表有 n 个节点,每 2 个节点抽取抽出一个节点作为上一级索引的节点,那每一级索引的节点数分别为: n/2 , n/4 , n/8 , … , 8 , 4 , 2 ,等比数列求和 n-1,所以跳表的空间复杂度为 O(n) 。

3.2 如何优化时间复杂度

如果链表有 n 个节点,每 3 或 5 个节点抽取抽出一个节点作为上一级索引的节点,那每一级索引的节点数分别为(以 3 为例): n/3 , n/9 , n/27 , … , 27 , 9 , 3 ,
1 ,等比数列求和 n/2 ,所以跳表的空间复杂度为 O(n) ,和每 2 个节点抽取一次相比,时间复杂度要低不少呢。

四、高效的动态插入和删除?

跳表本质上就是链表,所以仅插作,插入和删除操时间复杂度就为 O(1) ,但在实际情况中,要插入或删除某个节点,需要先查找到指定位置,而这个查找操作比较费时,但在跳表中这个查找操作的时间复杂度是 O(logn) ,所以,跳表的插入和删除操作的是时间复杂度也是 O(logn) 。

五、跳表索引动态更新?

当往跳表中插入数据的时候,可以选择同时将这个数据插入到部分索引层中,那么如何选择这个索引层呢?可以通过随机函数来决定将这个节点插入到哪几级索引中,比如随机函数生成了值 K ,那就可以把这个节点添加到第 1 级到第 K 级索引中。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值