zoukankan      html  css  js  c++  java
  • Skip List(跳跃表)原理详解与实现

    ref : https://dsqiu.iteye.com/blog/1705530


     

    本文内容框架:

    §1 Skip List 介绍

    §2 Skip List 定义以及构造步骤

      §3 Skip List 完整实现

     

    §4 Skip List 概率分析

    §5 小结

     

    §1 Skip List 介绍

    Skip List是一种随机化的数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间)。基本上,跳跃列表是对有序的链表增加上附加的前进链接,增加是以随机化的方式进行的,所以在列表中的查找可以快速的跳过部分列表(因此得名)。所有操作都以对数随机化的时间进行。Skip List可以很好解决有序链表查找特定值的困难。

     

    §2 Skip List 定义以及构造步骤

    Skip List定义

    像下面这样(初中物理经常这样用,这里我也盗用下):

    一个跳表,应该具有以下特征:

    1. 一个跳表应该有几个层(level)组成;
    2. 跳表的第一层包含所有的元素;
    3. 每一层都是一个有序的链表;
    4. 如果元素x出现在第i层,则所有比i小的层都包含x;
    5. 第i层的元素通过一个down指针指向下一层拥有相同值的元素;
    6. 在每一层中,-1和1两个元素都出现(分别表示INT_MIN和INT_MAX);
    7. Top指针指向最高层的第一个元素。

    构建有序链表

    link list

    的一个跳跃表如下:

     

    Skip List构造步骤:

           1、给定一个有序的链表。

    2、选择连表中最大和最小的元素,然后从其他元素中按照一定算法(随机)随即选出一些元素,将这些元素组成有序链表。这个新的链表称为一层,原链表称为其下一层。
    3、为刚选出的每个元素添加一个指针域,这个指针指向下一层中值同自己相等的元素。Top指针指向该层首元素
    4、重复2、3步,直到不再能选择出除最大最小元素以外的元素。

    §3 Skip List 完整实现

    下面来定义跳表的数据结构(基于C)

    首先是每个节点的数据结构

    C代码  收藏代码
    1. typedef  struct nodeStructure  
    2. {  
    3.   
    4.     int key;  
    5.   
    6.     int value;  
    7.   
    8.     struct nodeStructure *forward[1];  
    9. }nodeStructure;  
     

    跳表的结构如下

    C代码  收藏代码
    1. typedef  struct skiplist  
    2. {  
    3.   
    4.     int level;  
    5.   
    6.     nodeStructure *header;  
    7. }skiplist;  

    下面是跳表的基本操作

    首先是节点的创建

    C代码  收藏代码
    1. nodeStructure* createNode(int level,int key,int value)  
    2. {  
    3.   
    4.     nodeStructure *ns=(nodeStructure *)malloc(sizeof(nodeStructure)+level*sizeof(nodeStructure*));    
    5.   
    6.     ns->key=key;    
    7.   
    8.     ns->value=value;    
    9.   
    10.     return ns;    
    11. }  
     

    列表的初始化

    列表的初始化需要初始化头部,并使头部每层(根据事先定义的MAX_LEVEL)指向末尾(NULL)。

    C代码  收藏代码
    1. skiplist* createSkiplist()  
    2. {  
    3.   
    4.     skiplist *sl=(skiplist *)malloc(sizeof(skiplist));    
    5.   
    6.     sl->level=0;    
    7.   
    8.     sl->header=createNode(MAX_LEVEL-1,0,0);    
    9.   
    10.     for(int i=0;i<MAX_LEVEL;i++)    
    11.   
    12.     {    
    13.   
    14.         sl->header->forward[i]=NULL;    
    15.   
    16.     }  
    17.   
    18.     return sl;  
    19. }  

    插入元素

    插入元素的时候元素所占有的层数完全是随机的,通过随机算法产生

     

    C代码  收藏代码
    1. int randomLevel()    
    2. {  
    3.   
    4.     int k=1;  
    5.   
    6.     while (rand()%2)    
    7.   
    8.         k++;    
    9.   
    10.     k=(k<MAX_LEVEL)?k:MAX_LEVEL;  
    11.   
    12.     return k;    
    13. }  

     

     

    跳表的插入需要三个步骤,第一步需要查找到在每层待插入位置,然后需要随机产生一个层数,最后就是从高层至下插入,插入时算法和普通链表的插入完全相同。

    跳表,Skip List

    C代码  收藏代码
    1. bool insert(skiplist *sl,int key,int value)  
    2. {  
    3.   
    4.     nodeStructure *update[MAX_LEVEL];  
    5.   
    6.     nodeStructure *p, *q = NULL;  
    7.   
    8.     p=sl->header;  
    9.   
    10.     int k=sl->level;  
    11.   
    12.     //从最高层往下查找需要插入的位置  
    13.   
    14.     //填充update  
    15.   
    16.     for(int i=k-1; i >= 0; i--){  
    17.   
    18.         while((q=p->forward[i])&&(q->key<key))  
    19.   
    20.         {  
    21.   
    22.             p=q;  
    23.   
    24.         }  
    25.   
    26.         update[i]=p;  
    27.   
    28.     }  
    29.   
    30.     //不能插入相同的key  
    31.   
    32.     if(q&&q->key==key)  
    33.   
    34.     {  
    35.   
    36.         return false;  
    37.   
    38.     }  
    39.   
    40.     
    41.   
    42.     //产生一个随机层数K  
    43.   
    44.     //新建一个待插入节点q  
    45.   
    46.     //一层一层插入  
    47.   
    48.     k=randomLevel();  
    49.   
    50.     //更新跳表的level  
    51.   
    52.     if(k>(sl->level))  
    53.   
    54.     {  
    55.   
    56.         for(int i=sl->level; i < k; i++){  
    57.   
    58.             update[i] = sl->header;  
    59.   
    60.         }  
    61.   
    62.         sl->level=k;  
    63.   
    64.     }  
    65.   
    66.     
    67.   
    68.     q=createNode(k,key,value);  
    69.   
    70.     //逐层更新节点的指针,和普通列表插入一样  
    71.   
    72.     for(int i=0;i<k;i++)  
    73.   
    74.     {  
    75.   
    76.         q->forward[i]=update[i]->forward[i];  
    77.   
    78.         update[i]->forward[i]=q;  
    79.   
    80.     }  
    81.   
    82.     return true;  
    83. }  

     

     红色区域为辅助数组update的内容

     

    删除节点

    删除节点操作和插入差不多,找到每层需要删除的位置,删除时和操作普通链表完全一样。不过需要注意的是,如果该节点的level是最大的,则需要更新跳表的level。

     

    C代码  收藏代码
    1. bool deleteSL(skiplist *sl,int key)  
    2. {  
    3.   
    4.     nodeStructure *update[MAX_LEVEL];  
    5.   
    6.     nodeStructure *p,*q=NULL;  
    7.   
    8.     p=sl->header;  
    9.   
    10.     //从最高层开始搜  
    11.   
    12.     int k=sl->level;  
    13.   
    14.     for(int i=k-1; i >= 0; i--){  
    15.   
    16.         while((q=p->forward[i])&&(q->key<key))  
    17.   
    18.         {  
    19.   
    20.             p=q;  
    21.   
    22.         }  
    23.   
    24.         update[i]=p;  
    25.   
    26.     }  
    27.   
    28.     if(q&&q->key==key)  
    29.   
    30.     {  
    31.   
    32.         //逐层删除,和普通列表删除一样  
    33.   
    34.         for(int i=0; i<sl->level; i++){    
    35.   
    36.             if(update[i]->forward[i]==q){    
    37.   
    38.                 update[i]->forward[i]=q->forward[i];    
    39.   
    40.             }  
    41.   
    42.         }   
    43.   
    44.         free(q);  
    45.   
    46.         //如果删除的是最大层的节点,那么需要重新维护跳表的  
    47.   
    48.         for(int i=sl->level-1; i >= 0; i--){    
    49.   
    50.             if(sl->header->forward[i]==NULL){    
    51.   
    52.                 sl->level--;    
    53.   
    54.             }    
    55.   
    56.         }    
    57.   
    58.         return true;  
    59.   
    60.     }  
    61.   
    62.     else  
    63.   
    64.         return false;  
    65. }  

     

     

    查找

    跳表的优点就是查找比普通链表快,当然查找操作已经包含在在插入和删除过程,实现起来比较简单。

    跳表,Skip List

     搜索key=14的示意图

     
    C代码  收藏代码
    1. int search(skiplist *sl,int key)  
    2. {  
    3.   
    4.     nodeStructure *p,*q=NULL;  
    5.   
    6.     p=sl->header;  
    7.   
    8.     //从最高层开始搜  
    9.   
    10.     int k=sl->level;  
    11.   
    12.     for(int i=k-1; i >= 0; i--){  
    13.   
    14.         while((q=p->forward[i])&&(q->key<=key))  
    15.   
    16.         {  
    17.   
    18.             if(q->key==key)  
    19.   
    20.             {  
    21.   
    22.                 return q->value;  
    23.   
    24.             }  
    25.   
    26.             p=q;  
    27.   
    28.         }  
    29.   
    30.     }  
    31.   
    32.     return NULL;  
    33. }  

     

    完整代码如下:

    C代码  收藏代码
    1. #include<stdio.h>  
    2. #include<stdlib.h>  
    3.     
    4. #define MAX_LEVEL 10 //最大层数  
    5.     
    6. //节点  
    7. typedef  struct nodeStructure  
    8. {  
    9.     int key;  
    10.     int value;  
    11.     struct nodeStructure *forward[1];  
    12. }nodeStructure;  
    13.     
    14. //跳表  
    15. typedef  struct skiplist  
    16. {  
    17.     int level;  
    18.     nodeStructure *header;  
    19. }skiplist;  
    20.     
    21. //创建节点  
    22. nodeStructure* createNode(int level,int key,int value)  
    23. {  
    24.     nodeStructure *ns=(nodeStructure *)malloc(sizeof(nodeStructure)+level*sizeof(nodeStructure*));    
    25.     ns->key=key;    
    26.     ns->value=value;    
    27.     return ns;    
    28. }  
    29.     
    30. //初始化跳表  
    31. skiplist* createSkiplist()  
    32. {  
    33.     skiplist *sl=(skiplist *)malloc(sizeof(skiplist));    
    34.     sl->level=0;    
    35.     sl->header=createNode(MAX_LEVEL-1,0,0);    
    36.     for(int i=0;i<MAX_LEVEL;i++)    
    37.     {    
    38.         sl->header->forward[i]=NULL;    
    39.     }  
    40.     return sl;  
    41. }  
    42.     
    43. //随机产生层数  
    44. int randomLevel()    
    45. {  
    46.     int k=1;  
    47.     while (rand()%2)    
    48.         k++;    
    49.     k=(k<MAX_LEVEL)?k:MAX_LEVEL;  
    50.     return k;    
    51. }  
    52.     
    53. //插入节点  
    54. bool insert(skiplist *sl,int key,int value)  
    55. {  
    56.     nodeStructure *update[MAX_LEVEL];  
    57.     nodeStructure *p, *q = NULL;  
    58.     p=sl->header;  
    59.     int k=sl->level;  
    60.     //从最高层往下查找需要插入的位置  
    61.     //填充update  
    62.     for(int i=k-1; i >= 0; i--){  
    63.         while((q=p->forward[i])&&(q->key<key))  
    64.         {  
    65.             p=q;  
    66.         }  
    67.         update[i]=p;  
    68.     }  
    69.     //不能插入相同的key  
    70.     if(q&&q->key==key)  
    71.     {  
    72.         return false;  
    73.     }  
    74.     
    75.     //产生一个随机层数K  
    76.     //新建一个待插入节点q  
    77.     //一层一层插入  
    78.     k=randomLevel();  
    79.     //更新跳表的level  
    80.     if(k>(sl->level))  
    81.     {  
    82.         for(int i=sl->level; i < k; i++){  
    83.             update[i] = sl->header;  
    84.         }  
    85.         sl->level=k;  
    86.     }  
    87.     
    88.     q=createNode(k,key,value);  
    89.     //逐层更新节点的指针,和普通列表插入一样  
    90.     for(int i=0;i<k;i++)  
    91.     {  
    92.         q->forward[i]=update[i]->forward[i];  
    93.         update[i]->forward[i]=q;  
    94.     }  
    95.     return true;  
    96. }  
    97.     
    98. //搜索指定key的value  
    99. int search(skiplist *sl,int key)  
    100. {  
    101.     nodeStructure *p,*q=NULL;  
    102.     p=sl->header;  
    103.     //从最高层开始搜  
    104.     int k=sl->level;  
    105.     for(int i=k-1; i >= 0; i--){  
    106.         while((q=p->forward[i])&&(q->key<=key))  
    107.         {  
    108.             if(q->key == key)  
    109.             {  
    110.                 return q->value;  
    111.             }  
    112.             p=q;  
    113.         }  
    114.     }  
    115.     return NULL;  
    116. }  
    117.     
    118. //删除指定的key  
    119. bool deleteSL(skiplist *sl,int key)  
    120. {  
    121.     nodeStructure *update[MAX_LEVEL];  
    122.     nodeStructure *p,*q=NULL;  
    123.     p=sl->header;  
    124.     //从最高层开始搜  
    125.     int k=sl->level;  
    126.     for(int i=k-1; i >= 0; i--){  
    127.         while((q=p->forward[i])&&(q->key<key))  
    128.         {  
    129.             p=q;  
    130.         }  
    131.         update[i]=p;  
    132.     }  
    133.     if(q&&q->key==key)  
    134.     {  
    135.         //逐层删除,和普通列表删除一样  
    136.         for(int i=0; i<sl->level; i++){    
    137.             if(update[i]->forward[i]==q){    
    138.                 update[i]->forward[i]=q->forward[i];    
    139.             }  
    140.         }   
    141.         free(q);  
    142.         //如果删除的是最大层的节点,那么需要重新维护跳表的  
    143.         for(int i=sl->level - 1; i >= 0; i--){    
    144.             if(sl->header->forward[i]==NULL){    
    145.                 sl->level--;    
    146.             }    
    147.         }    
    148.         return true;  
    149.     }  
    150.     else  
    151.         return false;  
    152. }  
    153.     
    154. void printSL(skiplist *sl)  
    155. {  
    156.     //从最高层开始打印  
    157.     nodeStructure *p,*q=NULL;  
    158.     
    159.     //从最高层开始搜  
    160.     int k=sl->level;  
    161.     for(int i=k-1; i >= 0; i--)  
    162.     {  
    163.         p=sl->header;  
    164.         while(q=p->forward[i])  
    165.         {  
    166.             printf("%d -> ",p->value);  
    167.             p=q;  
    168.         }  
    169.         printf(" ");  
    170.     }  
    171.     printf(" ");  
    172. }  
    173. int main()  
    174. {  
    175.     skiplist *sl=createSkiplist();  
    176.     for(int i=1;i<=19;i++)  
    177.     {  
    178.         insert(sl,i,i*2);  
    179.     }  
    180.     printSL(sl);  
    181.     //搜索  
    182.     int i=search(sl,4);  
    183.     printf("i=%d ",i);  
    184.     //删除  
    185.     bool b=deleteSL(sl,4);  
    186.     if(b)  
    187.         printf("删除成功 ");  
    188.     printSL(sl);  
    189.     system("pause");  
    190.     return 0;  
    191. }  

    §4 Skip List 概率分析




     

     

    §5 小结

    本篇博文已经详细讲解了Skip List数据结构的所有内容,应该可以有一个深入的了解。如果你有任何建议或者批评和补充,请留言指出,不胜感激,更多参考请移步互联网。

    参考:

    ①Skip List: http://www.cs.auckland.ac.nz/software/AlgAnim/niemann/s_skl.htm

    ②Songeliu: http://www.spongeliu.com/63.html

    Shi Kai Lun : http://yilee.info/skip-list.html

    ④Michael T. Goodrich Roberto Tamassia Algorithm Design Foundations, Analysis, and Internet Examples

    http://epaperpress.com/sortsearch/skl.html

  • 相关阅读:
    SQL Server, Timeout expired.all pooled connections were in use and max pool size was reached
    javascript 事件调用顺序
    Best Practices for Speeding Up Your Web Site
    C语言程序设计 使用VC6绿色版
    破解SQL Prompt 3.9的几步操作
    Master page Path (MasterPage 路径)
    几个小型数据库的比较
    CSS+DIV 完美实现垂直居中的方法
    由Response.Redirect引发的"Thread was being aborted. "异常的处理方法
    Adsutil.vbs 在脚本攻击中的妙用
  • 原文地址:https://www.cnblogs.com/schips/p/11098198.html
Copyright © 2011-2022 走看看