You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

61 lines
3.5 KiB

# 一、介绍
属于单链表的变体,解决了单链表查找慢的问题(单链表需要从头遍历并且无法使用二分查找因为没有指针或下标能找到中间位置)
跳跃列表的查找和插入都是O(logn)
# 二、原理
## 2.1 创建
由于链表无指针的特性导致不能实现快速查找,此时需要改进为跳跃列表,首先在头结点加入一个哨兵
![[Snipaste_2023-02-23_10-00-41 8.png]]
加入哨兵并把当前作为L0层
![[Snipaste_2023-02-23_10-00-41 9.png]]
开始构建L1层从头遍历每个元素每个元素有50%的概率上升到L1层如图所示结点2、10、11、13、20、26上升为L1层元素**同时保留在L0层的位置**
![[Snipaste_2023-02-23_10-00-41 10.png]]
构建L2层从头遍历L1层元素过程与L0到L1相同如图所示2、13、26从L1上升到L2层**同时保留在L1层的位置**根据概率论可以算出元素2来到L2层的概率为25%50%(L1)*50%(L2)=25%
![[Snipaste_2023-02-23_10-00-41 11.png]]
构建L3层从头遍历L2层元素过程与L1到L2相同
![[Snipaste_2023-02-23_10-00-41 12.png]]
跳跃的意义在于可以快速到达元素例如从L2层可以之间从2到13只需要2步而L1层从2到13需要4步L0层需要5步。
**跳跃列表的层数最好为Logn层**
## 2.2 查找
### 2.2.1 查找key=13
1. 从顶层L3开始查找从sentinel开始遍历从头开始到达第一个结点13
2. 将key=13与元素相比较结果相同说明已经找到该元素
![[Snipaste_2023-02-23_10-00-41 13.png]]
### 2.2.2查找key=8
1. 从顶层L3开始查找从sentinel开始遍历从头开始到达第一个结点13key<13,进入L2
2. 从L2层开始到达第一个结点2key>2说明key在2的右边。到达L2层第二个结点13key<13,进入L1
3. 从L1层开始到达第一个结点2key>2说明key在2的右边。到达L1层第二个结点10key<10,进入L0
4. 从L0层开始依次遍历找到key=8结束
![[Snipaste_2023-02-23_10-00-41 14.png]]
### 2.2.3 查找key=20
1. 从顶层L3开始查找从sentinel开始遍历从头开始到达第一个结点13key>13由于L3层只有13这个结点所有进入L2层。
2. 从L2层开始由于key>13所以从L2的13结点查找到达26key<26,说明key13~26之间,向下进入L1
3. 从L1层13结点开始查找向右找到Key=20结束
![[Snipaste_2023-02-23_10-00-41 15.png]]
## 2.3 插入
### 2.3.1 插入key=9
1. 查找前先找到插入位置过程看2.2节如图所示找到L0层需要4个步骤
![[Snipaste_2023-02-23_10-00-41 16.png]]
2. 记录每个步骤起点位置
![[Snipaste_2023-02-23_10-00-41 17.png]]
3. 创建新结点9高度随机随机次数由(层数-1)决定
![[Snipaste_2023-02-23_10-00-41 18.png]]
4. 将新结点与原有结点连接,此时新结点插入完毕,且进行了排序
![[Snipaste_2023-02-23_10-00-41 19.png]]
# 三、实践
跳跃列表用于Redis的zset作为有序去重队列使用
# 四、总结
特点:
1. 插入和查找都快
2. 有些元素可以同时处于多层例如13同时处于L3、L2、L1、L0层
3. 元素每次上升的概率都会乘以50%例如从L0到L1为50%L1到L2为50%那么从L0到L2为25%
4. 列表中元素越多能够进入的层次越深,进入顶层的概率越大
5. **一个元素如果位于顶层那么从L0到顶层都会存在例如结点13**
跳跃列表是一个金字塔结构从顶层到L0元素越来越少。如果查找的元素刚好位于顶层那么效率就很高