跳表有多跳? | 青训营笔记

117 阅读2分钟

这是我参与「第五届青训营」伴学笔记创作活动的第13天。本篇为第五届字节跳动青训营-寒假专场-后端基础课程的笔记。

引入

redis为什么采用跳表而不是红黑树?? 显然这样的事实暗示着跳表相较于红黑树在某些场景下更加高效,就让我们来了解一下跳表。

跳表 SkipList

跳表分为多层,每层都是数据的索引,上层是下层的子集。

传统单链表的查询速率是 O(N) 。跳表正是基于这个数据结构,增加了层数。我们从高层开始查找,如果值相等就找到了目标,或者我们可以缩小目标的范围,并且进入更深层进行查找(层数越深,包含的元素(索引)越多,查找到的几率就越大)。

从某种角度来看,跳表就是可以实现高效二分查找的有序链表。

假设跳表实际存储元素 N 个

  1. 其一级索引有 N/2N/2 个元素
  2. K 级索引有 N/2KN/2^K 个元素
  3. 总高度 为 log2Nlog_2{N}
  4. 最高级的索引层共有2个索引
  5. 查找时候,每层最多需要访问索引3次,这样查询时间复杂度就是 O(log2N)O(log_2{N})

跳表的插入

插入跳表,我们希望其时间复杂度是对数级别的,而且我们希望插入后跳表可以保持高效的查找能力。

  1. 首先,插入元素时,我们决定要为这个元素创建几级的索引
    1. 创建多少级的索引需要满足一定分布。
    2. 1级索引创建概率为 0.5
    3. 2级索引创建概率为 0.25
    4. 3级索引创建概率为 0.125
    5. 以此类推
  2. 从最高级的索引层开始执行插入,直到最底层
  3. 这样就完成了跳表的插入更新

跳表的删除

从上往下,边搜索,便删除索引。

问题解决

redis为什么采用跳表而不是红黑树??

Redis 中的有序集合(zset) 支持的操作:

  1. 插入一个元素

  2. 删除一个元素

  3. 查找一个元素

  4. 有序输出所有元素

  5. 按照范围区间查找元素 其中,前四个操作红黑树也可以完成,且时间复杂度跟跳表是一样的。按照区间来查找数据这个操作,跳表效率高。可以做到 O(logn) 的时间复杂度。

JUC——ConcurrentSkipListMap

ConcurrentSkipListMap是线程安全的有序的哈希表,适用于高并发的场景。

特点:

  1. 相比于HashMap key是有序的
  2. 相比于HashMap 支持更高的并发
  3. 在多线程程序中,如果需要对Map的键值进行排序时,请尽量使用ConcurrentSkipListMap,可能得到更好的并发度。