介绍
跳表是一种链表加多级索引的动态数据结构,具体来说是每两个结点提取一个结点到上一级作为索引,如下图所示:
所以跳表相对于普通单链表,查找一个结点所需要遍历的结点个数减少了,也即查找效率提高了。跳表是一种支持二分查找的链表,可以快速地插入、删除、查找,甚至可以替代红黑树。Redis里的有序集合就是用跳表来实现的。
复杂度分析
时间复杂度
在跳表中查找、插入、删除的时间复杂度都是
O
(
l
o
g
n
)
O(logn)
O(logn)。
1.查找的时间复杂度推导
- 查多少层:对于结点个数为n的链表,会有多少级索引?第一级有
n
2
\frac{n}{2}
2n?个结点,第二级有
n
2
2
\frac{n}{2^2}
22n?个结点,第k级有
n
2
k
\frac{n}{2^k}
2kn?个结点,最后一级是2个结点,所以通过
n
2
k
=
2
\frac{n}{2^k}=2
2kn?=2,得到最后一级
k
=
l
o
g
2
n
?
1
k=log_2n-1
k=log2?n?1。如果把原始链表也算作一级,那么最后一级就是
l
o
g
2
n
log_2n
log2?n,所以一共查
l
o
g
2
n
log_2n
log2?n层就能查到
- 每层内查几次:最多3次。因为上一层决定了所查找的数在结点j和j+1之间,才会下到下一层去找,而上一层的结点j和j+1之间对应到下一层,只有三个结点
- 总结:跳表的查找时间复杂度为
O
(
3
l
o
g
2
n
)
=
O
(
l
o
g
n
)
O(3log_2n)=O(log n)
O(3log2?n)=O(logn)
2.插入的时间复杂度推导
单链表的插入:查找
O
(
n
)
O(n)
O(n) + 插入
O
(
1
)
O(1)
O(1) 跳表的插入:查找
O
(
l
o
g
n
)
O(logn)
O(logn) + 插入
O
(
1
)
O(1)
O(1)
3.删除的时间复杂度推导
查找
O
(
l
o
g
n
)
O(logn)
O(logn)+删除
O
(
1
)
O(1)
O(1),删除时要注意:记住前驱结点、如果索引中有也要删除索引中的
空间复杂度
跳表的空间复杂度为
O
(
n
)
O(n)
O(n),具体推导如下:
- 在分析时间复杂度时已经提到,对于结点个数为n的链表,会有
l
o
g
2
n
?
1
log_2n-1
log2?n?1层索引,各层索引的结点数分别是
n
2
\frac{n}{2}
2n?,
n
2
2
\frac{n}{2^2}
22n?,…,
n
2
k
\frac{n}{2^k}
2kn?,…,2这样的等比数列
- 根据等比数列求和公式:
得到跳表的空间复杂度为
O
(
n
)
O(n)
O(n):
跳表的动态更新
跳表是一种动态数据结构,就是指它的索引是要动态更新的,特别是当插入了很多个结点、或者删除了一些结点时,可能会出现两个索引结点之间的下一层结点很多的情况,造成复杂度退化。
跳表通过随机函数来维护,当往跳表中插入数据的时候,可以选择同时将这个数据插入到部分索引层中,具体插入到从哪一级开始的索引是由一个随机函数生成的。
|