skiplist 跳表

SkipList跳表C++实现 跳跃列表(也称跳表)是一种随机化数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间)。 阅读详情

1、什么是SkipList

当使用链表存放有序数据的时候,查找某个数据或者添加某个数据的复杂度是O(n),每次查找都需要顺序遍历。加快搜索的方法有哪些呢?想想数据库的设计思路,对,使用索引,利用空间换取时间。跳表基本上采用的就是这个思路。


图1

在最底层的链表中随机抽取一定是数量的节点作为索引,形成另一个链表。然后继续从新生成的链表中随机抽取点作为新的索引,继续形成另一个链表。将这个步骤继续多次。
可以得到上面图中的结构。通过观察上面的图,可以发现越往上的链表越稀疏。如果对多叉树比较熟悉的话,可以将上面的结构立即为多叉树。因此可以猜测SkipList的时间复杂度是O(ogn)。

来看看SkipList如何工作的。


图2
由于上层的链表十分稀疏,所以上层链表遍历的时候可以忽略掉底层链表中许多的元素。比如上面的图中,插入的数据是80的时候,从30到50可以把40忽略掉。如果链表中的数据规模达到一定地步,上层链表的一次遍历可以跳过的元素是相当可观的。就像二叉树一样,没经过一个节点都可以跳过当前元素的一般。而由于SkipList每层链表都是按照一定概率生成的,虽然不是像二叉树那样经过一个节点可以忽略一半元素,但是效果也还是不错的。

2、SkipList中的概念

2.1、Node

每个节点有一个值,用于排序。并且有一个数组用于存放后面的节点。比如图1中,节点6的nexts数组大小是4, 数组的数据依次指向节点7,节点9,节点25,NIL节点。


2.2、SkipList

跳表中有一个head节点,同时设置一个level表示当前最大的层数。比如图2中,level = 4。

而且每个跳表都有一个常量的MAX_LEVEL,链表层数无论如何增加也不能超过这个值。

2.3、NIL

NIL值用于表示比任何值都大。一般存放在每个链表的结尾。表示当前链表搜索可以结束了。

3、SkipList的初始化

An element NIL is given a key greater than any legal key. All levels of all skip lists are terminated with NIL.A new list is initialized so that the levelof the list is equal to 1 and all forward pointers of the list’s header point to NIL.
(摘自论文  A Skip List Cookbook  Author: William Pugh)

4、SkipList的搜索

We search for an element by traversing forward pointers that do not overshoot the node containing the element being searched for (Figure 2). When no more progress can be made at the current level of forward pointers, the search moves down to the next level. When we can make no more progress at level 1, we must be immediately in front of the node that contains the desired element (if it is in the list).

[java]  view plain  copy
  1. Search(list, searchKey)  
  2.     x := list→header  
  3.     -- loop invariant: x→key < searchKey  
  4.     for i := list→level downto1 do  
  5.         whilex→forward[i]→key < searchKey do  
  6.             x := x→forward[i]  
  7.     -- x→key < searchKey ≤x→forward[1]→key  
  8.     x := x→forward[1]  
  9.     if x→key = searchKey then returnx→value  
  10.     else return failure  

5、SkipList的插入和删除

To insert or delete a node, we simply search and splice.Figure 3 gives algorithms for insertion and deletion. A vector updateis maintained so that when the search is complete (and we are ready to perform the splice), update[i] contains a pointer to the rightmost node of level ior higher that is to the left of the location of the insertion/deletion. If an insertion generates a node with a level greater than the previous maximum level of the list, we update the maximum level of the list and initialize the appropriate portions of the update vector. After each deletion, we check if we have deleted the maximum element of the list and if so, decrease the maximum level of the list.

[java]  view plain  copy
  1. Insert(list, searchKey, newValue)  
  2.     localupdate[1..MaxLevel]  
  3.     x := list→header  
  4.     for i := list→level down to 1 do  
  5.         whilex→forward[i]→key < searchKey do  
  6.             x := x→forward[i]  
  7.         update[i] := x  
  8.     x := x→forward[1]  
  9.     if x→key = searchKey then x→value := newValue  
  10.     else  
  11.         newLevel := randomLevel()  
  12.         if newLevel > list→level then  
  13.             for i := list→level + 1 to newLevel do  
  14.                 update[i] := list→header  
  15.             list→level := newLevel  
  16.         x := makeNode(newLevel , searchKey, value)  
  17.         for i := 1 tonewLevel do  
  18.             x→forward[i] := update[i]→forward[i]  
  19.             update[i]→forward[i] := x  

[java]  view plain  copy
  1. Delete(list, searchKey)  
  2.     local update[1..MaxLevel]  
  3.     x := list→header  
  4.     for i := list→level down to 1 do  
  5.         while x→forward[i]→key < searchKey do  
  6.             x := x→forward[i]  
  7.         update[i] := x  
  8.     x := x→forward[1]  
  9.     if x→key = searchKey then  
  10.         for i := 1 to list→level do  
  11.             if update[i]→forward[i] ≠ x then break  
  12.             update[i]→forward[i] := x→forward[i]  
  13.         free(x)  
  14.         while list→level > 1 and list→header→forward[list→level] = NIL do  
  15.             list→level := list→level – 1      

6、新插入节点随机层数算法

[java]  view plain  copy
  1. randomLevel()  
  2.     lvl := 1  
  3.     while random() < p and lvl < MaxLevel do  
  4.         lvl := lvl + 1  
  5.     return lvl  

这里设置p为1/2。
如果random()函数设计的足够好,那么每次random()产生的数据都有50%的概率小于1/2。也就是说层数为1的概率为100%,层数为2的概率为50%,层数为3的概率为25%,层数为4的概率为12.5%,依次衰减。所以可以看出越往上的链表,其中的节点数也就越少。

7、java版本的实现

[java]  view plain  copy
  1. import java.util.ArrayList;  
  2. import java.util.List;  
  3.   
  4.   
  5. public class Node<T extends Comparable<T>> {  
  6.     private T value;  
  7.     private List<Node<T>> nexts;  
  8.       
  9.     public Node(){  
  10.         this.value = null;  
  11.         this.nexts = new ArrayList<Node<T>>(0);  
  12.     }  
  13.       
  14.     public Node(T value){  
  15.         this.value = value;  
  16.         this.nexts = new ArrayList<Node<T>>(0);  
  17.     }  
  18.       
  19.     public Node(int size){  
  20.         this.value = null;  
  21.         this.nexts = new ArrayList<Node<T>>(size);  
  22.     }  
  23.       
  24.     public void setValue(T value) {  
  25.         this.value = value;  
  26.     }  
  27.     public T getValue() {  
  28.         return value;  
  29.     }  
  30.     public void setNexts(List<Node<T>> nexts) {  
  31.         this.nexts = nexts;  
  32.     }  
  33.     public List<Node<T>> getNexts() {  
  34.         return nexts;  
  35.     }  
  36. }  

注意这里的nexts数组就是伪代码中的forward数组。

[java]  view plain  copy
  1. public class SkipList<T extends Comparable<T>> {  
  2.     public static final int MAX_LEVEL = 100;  
  3.     // the head of the SkipList  
  4.     private Node<T> head = null;  
  5.   
  6.     private int level = 0;  
  7.     // initial of the SkipList  
  8.     public SkipList() {  
  9.         // 初始化头部  
  10.         setHead(new Node<T>(MAX_LEVEL));  
  11.   
  12.         // 初始化head的nexts数组  
  13.         for (int i = 0; i < MAX_LEVEL; i++) {  
  14.             head.getNexts().add(null);  
  15.         }  
  16.           
  17.         // 设置深度  
  18.         setLevel(1);  
  19.     }  
  20.   
  21.     @SuppressWarnings("unchecked")  
  22.     public void insert(T newValue) {  
  23.         if (newValue == null) {  
  24.             return;  
  25.         }  
  26.         // 更新数组  
  27.         Node<T> update[] = new Node[MAX_LEVEL];  
  28.         // 获得头部  
  29.         Node<T> p = this.head;  
  30.   
  31.         int j = this.level - 1;  
  32.         while (j >= 0) {  
  33.             // 下一个节点不为NULL 并且  值小于newValue  
  34.             while ((Node<T>) p.getNexts().get(j) != null  
  35.                     && newValue.compareTo(((Node<T>) p.getNexts().get(j))  
  36.                             .getValue()) >= 0) {  
  37.                 p = (Node<T>) p.getNexts().get(j);  
  38.             }  
  39.             update[j] = p;  
  40.             j--;  
  41.         }  
  42.   
  43.         int newLevel = randomLevel();  
  44.   
  45.         if (newLevel > this.level) {  
  46.             // 当newLevel大于当前的level时,设置更新数组中level到newLevel为head  
  47.             for (int i = newLevel - 1; i > level - 1; i--) {  
  48.                 update[i] = this.head;  
  49.             }  
  50.   
  51.             // 当newLevel大于当前的level时,需要更新当前level  
  52.             this.level = newLevel;  
  53.         }  
  54.   
  55.         Node<T> newNode = new Node<T>();  
  56.         newNode.setValue(newValue);  
  57.   
  58.         for (int i = 0; i < newLevel; i++) {  
  59.             newNode.getNexts().add(update[i].getNexts().get(i));  
  60.             update[i].getNexts().set(i, newNode);  
  61.         }  
  62.           
  63.     }  
  64.       
  65.     //获取随机层数  
  66.     private int randomLevel() {  
  67.         int level = 1;  
  68.         while (Math.random() < 1.0 / 2.0 && level < MAX_LEVEL) {  
  69.             level++;  
  70.         }  
  71.         return level;  
  72.     }  
  73.     public void setHead(Node<T> head) {  
  74.         this.head = head;  
  75.     }  
  76.     public Node<T> getHead() {  
  77.         return head;  
  78.     }  
  79.   
  80.     public void setLevel(int level) {  
  81.         this.level = level;  
  82.     }  
  83.   
  84.     public int getLevel() {  
  85.         return level;  
  86.     }  
  87.   
  88.     public static void main(String[] args) {  
  89.         SkipList<Integer> skip = new SkipList<Integer>();  
  90.   
  91.         for (int i = 0; i < 10; i++) {  
  92.             skip.insert(new Integer(i));  
  93.         }  
  94.     }  
  95. }  

转自:http://blog.youkuaiyun.com/xlnjulp/article/details/25376015

SkipList跳表:高效查找的利器 自己实现一个SkipList 阅读详情

相关推荐

大白话解析LevelDB:SkipList(跳表)

LevelDB使用SkipList作为有序集合的实现。相比于红黑树、B+Tree,SkipList的实现简单,写入操作高效,唯一的缺点是最坏情况下其读写的时间复杂度是O(N)。

sinat_38293503的博客 2428

SkipList(跳跃表)详解

Introduction: skiplist本质上也是一种查找结构,用于解决算法中的查找问题(Searching),即根据给定的key,快速查到它所在的位置(或者对应的value) 一般用于解决查找问题的数据结构分为两个大类:一个是基于各种平衡树,一个是基于哈希表。但skiplist却比较特殊,它没法归属到这两大类里面 这种数据结构是由William Pugh发明的,最早出现于他在1990年发表的...

程序员の墨梅 1万+

leveldb 源码分析 —— SkipList跳表

leveldb 存取数据,都在用 MemTable 这个结构体,而 MemTable 核心在于 level::MemTable::Table,也就是 typedef SkipList<const char*, KeyComparator> level::MemTable::Table。 SkipList 看名字就知道,跳表,是一种数据结构,允许快速查询一个有序连续元素的数据链表。这是一种 “以空间换

Abson的博客 1260

每日一博 - 如何理解跳表(SkipList)

文章目录什么是跳跃表SkipList 什么是跳跃表SkipList 跳跃表(简称跳表)由美国计算机科学家William Pugh于1989年发明 论文: Skip lists: a probabilistic alternative to balanced trees 跳表(SkipList,全称跳跃表)是用于有序元素序列快速搜索查找的一个数据结构,跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。 跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。 跳表不仅

小工匠 2万+

A skip list cookbook.

介绍skip list实现的文档

2025华东杯A/B/C题解题思路+可运行代码参考

这些因素包括但不限于:助滑坡的坡度、运动员的质量、滑雪板的接触面与雪地的摩擦力、空气阻力系数、运动员的体型和衣物形态等。助滑坡是运动员滑行至起跳台前的最后一段坡道,这一段坡道的坡度、长度和运动员的姿势会直接影响到起跳时的初始速度。运动员的滑行速度不仅受助滑坡的坡度影响,还受到摩擦力、空气阻力和重力等多方面因素的影响。通过合理的姿势调整,运动员能够有效减少空气阻力,增加身体的重力加速度,进而提升滑行速度。助滑坡的角度通常介于10°到12°之间,而运动员的滑行速度将受到重力、摩擦力和空气阻力的共同作用。

qq_52590045的博客 1796

SkipList

1、什么是SkipList 当使用链表存放有序数据

xlnjulp Stay Hungry, Stay Foolish 937

ALSA音频开发实战:如何用snd_pcm_avail避免缓冲区溢出(附完整代码)

本文深入探讨了在ALSA音频开发中,如何利用`snd_pcm_avail`函数有效管理环形缓冲区,从而避免音频数据溢出或下溢。通过实战代码示例,详细解析了该函数在播放场景下的核心作用,即实时查询可写入的帧数,并构建了自适应写入循环以实现稳健的流量控制,确保音频流的平滑与稳定。

weixin_29183637的博客 461

浅析SkipList跳跃表原理及代码实现

SkipList在leveldb以及lucence中都广为使用,是比较高效的数据结构。由于它的代码以及原理实现的简单性,更为人们所接受。我们首先看看SkipList的定义,为什么叫跳跃表? “ Skip lists are data structures that use probabilistic balancing rather than strictly enforced balancing. As a result, the algorithms for insertion

庖丁解牛 5万+

基于STM32与HRTIM的四开关Buck-Boost双向DC-DC电源设计实战(附源码、仿真与设计解析)

本文详细介绍了基于STM32F334的HRTIM高分辨率定时器实现四开关Buck-Boost双向DC-DC电源的设计实战。通过精准的PWM控制、双环PID算法和智能模式切换逻辑,解决了电压震荡与效率优化等关键问题,并提供了完整的源码、PSIM仿真及参数计算,为高性能电源设计提供了实用参考。

7z8x2c4v6b9的博客 299

Android无线调试:adb tcpip连接实战指南

本文详细介绍了Android无线调试的完整流程,重点讲解如何使用adb tcpip命令通过Wi-Fi连接设备,摆脱数据线束缚。涵盖环境配置、连接实战、稳定性优化及常见问题排查,帮助开发者高效实现无线adb调试,提升工作效率。

pink的博客 721

高级数据结构——跳表skiplist

跳表是redis采用的实现有序集合的数据结构之一, 具有查询效率高和实现相对简单的优势。本文介绍了跳表的原理和代码实现,参考的是《redis5设计与源码分析》这本书。

leedcanDD的博客 1140

药物代谢仿真软件:PKSim_(6).案例研究与实践应用

通过本章的案例研究和实践应用,我们展示了如何利用PKSim软件构建药物动力学模型、进行参数估计、仿真预测和结果分析。这些方法不仅有助于优化药物代谢研究过程,还可以在药物研发中提供重要的数据支持。通过模型的构建和验证,我们可以更好地理解药物在体内的动态变化过程,为临床试验和药物治疗提供科学依据。

kkchenjj的博客 655

5.跳表(skiplist)

17比20小,往17跳;拿到需要删除的节点的指针del,为prevV[0]的第一层的下一个。分析:类似查找逻辑,从头结点出发,每次从最高层开始,但不同的是如果遇到相等的值,也要往下走(因为要记录前一个),并记录前一个。以此类推,我们可以在第二层新产生的链表上,继续为每相邻的两个节点升高一层,增加一个指针,从而产生第三层链表。跳表用基于概率的平衡而不是严格的平衡,事实上,插入和删除在跳表中比起等效平衡树的算法更简单,而且更快。式,上面每一层链表的节点个数,是下面一层的节点个数的一半,这样查找过程就非常。

2301_79790088的博客 663

Unity 实战项目 ☀️| 只用一个脚本做一个 刮刮乐 案例,一不小心刮出来一个女朋友!【学习娱乐一下】

只用一个脚本做一个刮刮乐,一不小心刮出来一个女朋友!【学习娱乐一下!】

努力前行,总会成为自己心中的那道光 23万+

【高阶数据结构】跳表-skiplist

【高阶数据结构】跳表-skiplist

qq_67582098的博客 1360

YOLOv8 改进:添加 LSK 注意力机制

通过添加 LSK 注意力机制,YOLOv8 能够更有效地捕捉特征图上的重要信息,提高检测性能,尤其是在复杂场景下展现出色的表现。这种方法不仅适用于资源受限的设备,还能在丰富多样的实际应用中提供强劲支持。

走向CTO的路上... 1221

skiplist - 跳表

跳表(skiplist、跳跃表) 是一个很优秀的数据结构,比如用于 Redis、levelDB等出名的开源项目上。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。它的结构特点在名称能很好的体现出来,就像兔子一样,蹦蹦跳跳。可以结合下面的图有个初步的印象。是不是看起来很形象了,接下来看看 Redis 是怎么实现跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。...

一七令 998

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

关于 SkipList (跳表)的一些资料梳理

关于 SkipList (跳表)的一些资料梳理 简介 SkipList(译为跳表,又称跳跃表); 跳跃列表是一种数据结构。它允许快速查询一个有序连续元素的数据链表。跳跃列表的平均查找和插入时间复杂度都是O(log n),优于普通队列的O(n)。 快速查询是通过维护一个多层次的链表,且每一层链表中的元素是前一层链表元素的子集。一开始时,算法在最稀疏的层次进行搜索,直至需要查找的元素在该层两个相邻的元...

晨猫 247

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

跳表-skiplist的简单实现

跳表-skiplist的简单实现

冯同学的博客 2767

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

C++跳表实现,封装成Skiplist类

C++手搓跳表,封装成Skiplist类(),性能强大(附题目和讲解链接)

2301_78689948的博客 879

【数据结构】跳表Skiplist

跳表原理,实现和与哈希表平衡树的比较

qq_53893431的博客 1391

跳表(SkipList)是什么

跳表(SkipList)是什么

weixin_45428910的博客 706

跳表SkipList

【代码】跳表SkipList。

creator_Li的博客 318

跳表 skiplist 简介

跳表是一个包含随机算法的数据结构,跳表的查找、插入、删除的平均时间复杂度都是 O(logn),而且可以按照范围区间查找元素,空间复杂度是 O(n)。

苏木川的博客 1544
上一篇: Java中的ArrayList的初始容量和容量分配
下一篇: 推荐 apache kafka技术分享系列(目录索引):
大树叶
博客等级 码龄12年 1171粉丝 664原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值