跳表的实现

跳表

1 概念

对于一个有n个元素的有序数组,用折半搜索法进行搜索所需要的时间为Olog n),而对一个有序链表进行搜索所需要的时间为On)。 

我们可以通过对有序链表上的全部或部分节点增加额外的指针,来提高搜索性能。 通常节点结构中有一组有层次的链,0级链是包含所有元素的有序链表,1级链表是0级链的一个子集。I级链所包含的元素是i-1级链的子集。这样的结构就是跳表。

增加了向前指针的链表叫作跳表。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点中应增加多少个指针。

2 级的分配:

i-1级元素属于i级元素的概率为p p通常取0.5

MaxLeve最大值:log1/p(N) - 1;  N是元素个数

CutOff=p*RAND_MAX 

Int lev=0;

While(rand()<=CutOff) lev++; 

如:

template<class E, class K>

Int SkipList<E,K>::Level()

{ //产生一个随机级号,该级号<=MaxLevel

 Int lev=0;

 While(rand()<=CutOff) lev++;

 Return (lev <= MaxLevel)?lev:MaxLevel;

 }

或如下:

  long newLevel = 0;

  while ( (newLevel < maxLevel - 1) && (drand48() < probability) ) {

    newLevel++;

  }

3 复杂性分析

跳表中有n个元素时,搜索,插入,删除操作的平均复杂性为O(log n).

  1. /*
  2.  * File :skiplist.h
  3.  * Interface for SkipList and SkipListElement
  4.  * Author: cxf
  5.  * Version: 1.0
  6.  * Date: 02/08/2008
  7.  * History:
  8.    02/08/2008; cxf; Version 1.0
  9.  */
  10. #ifndef _SKIPLIST_H_
  11. #define _SKIPLIST_H_
  12. #define MAXLEVEL 6 //节点中的最大级
  13. #define SKIPLIST_NOT_FOUND -1 //在跳表中没有找到相应的节点时,返回的值
  14. typedef int Key;   //键的类型
  15. typedef int Value; //值的类型
  16. /* 跳表节点 */
  17. class SkipListElement
  18. {
  19. public:
  20.     SkipListElement(int _level = 0, Key _key = 0, Value _value = 0);
  21.     ~SkipListElement();
  22.     void SetValue(Value _value);
  23.     Value GetValue() const;
  24.     void SetKey(Key _key);
  25.     Key GetKey() const;
  26.     void SetLevel(int _level);
  27.     int GetLevel() const;
  28.     void SetElement(int _level, SkipListElement *_next);
  29.     SkipListElement* GetElement(int _level) const;
  30. private:
  31.     Key key; //键
  32.     Value value; //值
  33.     int level;  //本节点的最大级,共有level + 1级,从0到level
  34.     SkipListElement **next;
  35. };
  36. /* 跳表类 */
  37. class SkipList
  38. {
  39. public:
  40.     SkipList(SkipListElement *_head = NULL);
  41.     ~SkipList();
  42.     void Insert(const Key _key, const Value _value);
  43.     Value Search(const Key _key);
  44.     void Free(Key _key);
  45.     friend std::ostream& operator<<(std::ostream outconst SkipList &slist);
  46. private:
  47.     int GetNewLevel();
  48.     SkipListElement *head; //跳表的头节点
  49. };
  50. #endif
  51. /*
  52.  * File :skiplist.cpp
  53.  * Implemention for SkipList and SkipListElement
  54.  * Author: cxf
  55.  * Version: 1.0
  56.  * Date: 02/08/2008
  57.  * History:
  58.  * 02/08/2008; cxf; Version 1.0
  59.  */
  60. #include <cassert> //assert
  61. #include <climits> //INT_MAX
  62. #include <iostream> 
  63. #include <ctime>   //time
  64. #include "skiplist.h"
  65. using namespace std;
  66. SkipListElement::SkipListElement(int _level /* = 0 */, Key _key /* = 0 */, /
  67.                                  Key _value /* = 0 */)
  68. {
  69.     level = _level;
  70.     key = _key;
  71.     value = _value;
  72.     //动态分配指针数组空间
  73.     next = (SkipListElement**)malloc(sizeof(SkipListElement*) * (level + 1));
  74.     if (NULL == next)
  75.     {
  76.         cout << __FILE__ << ":" << __LINE__ << " " << "no enough memory!" << endl;
  77.         exit(-1);
  78.     }
  79.     for (int i = 0; i <= level; i++)
  80.     {
  81.         next[i] = NULL;
  82.     }
  83. }
  84. SkipListElement::~SkipListElement()
  85. {
  86.     free(next);
  87. }
  88. void SkipListElement::SetKey(Key _key)
  89. {
  90.     key = _key;
  91. }
  92. void SkipListElement::SetValue(Value _value)
  93. {
  94.     value = _value;
  95. }
  96. void SkipListElement::SetElement(int _level, SkipListElement *_next)
  97. {   
  98.     assert(_level >= 0 && _level <= level);
  99.     next[_level] = _next;
  100. }
  101. SkipListElement* SkipListElement::GetElement(int _level) const
  102. {
  103.     assert(_level >= 0 && _level <= level);
  104.     return next[_level];
  105. }
  106. Key SkipListElement::GetKey() const
  107. {
  108.     return key;
  109. }
  110. Value SkipListElement::GetValue() const
  111. {
  112.     return value;
  113. }
  114. int  SkipListElement::GetLevel() const
  115. {
  116.     return level;
  117. }
  118. /* 若级别比当前的高,需要重新分配空间,否则,不必释放空间,留作以后用*/
  119. void SkipListElement::SetLevel(int _level)
  120. {
  121.     if (_level > level)
  122.     {
  123.         //重新分配空间
  124.         next = (SkipListElement**)realloc(next, (_level + 1) * sizeof(void *)); 
  125.         if (NULL == next)
  126.         {
  127.             cout << __FILE__ << ":" << __LINE__ << " " << "no enough memory!" << endl;
  128.             exit(-1);
  129.         }
  130.         //初始化增加的空间
  131.         memset(next + (level + 1), 0, sizeof(void *) * (_level - level));
  132.     }
  133.     level = _level;
  134. }
  135. /*===========================以下为SkipList部分===================================*/
  136. SkipList::SkipList(SkipListElement *_head /* = NULL */)
  137. {
  138.     if (NULL == _head)
  139.     {
  140.         head = new SkipListElement();
  141.         //头节点的key设置为无限大
  142.         head->SetKey(INT_MAX);
  143.     }
  144.     else
  145.     {
  146.         head = _head;
  147.     }
  148. }
  149. SkipList::~SkipList()
  150. {
  151.     SkipListElement *element = head;
  152.     //从级0开始,依次删除所有的节点
  153.     while (element != NULL)
  154.     {
  155.         SkipListElement *temp = element->GetElement(0);
  156.         delete element;
  157.         element = temp;
  158.     }
  159. }
  160. /* 随机产生一个级,为产生的新节点用 */
  161. int SkipList::GetNewLevel()
  162. {
  163.     static int i = 0;
  164.     /* 因为插入新节点的时间间隔几乎为零,为产生不同的随机数,加入静态变量
  165.      * 以使每次该函数被调用时种子不同
  166.      */
  167.     srand(time(0) + i); 
  168.     i += 20;
  169.     return rand() % MAXLEVEL + 1;
  170. }
  171. /* 将键为_key,值为_value的节点插入到跳表中
  172.  * 算法思想:可将跳表看做是一系列普通的链表,在跳表中插入节点,就是寻找插
  173.  * 入节点在每一级中的插入位置(并保存), 然后根据插入节点的级n,在0-n级的n+1
  174.  * 条普通链表中依次插入节点
  175.  */
  176. void SkipList::Insert(const Key _key, const Value _value)
  177. {
  178.     SkipListElement *elemnet = head;
  179.     SkipListElement *next = NULL;
  180.     SkipListElement  update(MAXLEVEL);//保存每一级插入位置
  181.     int newLevel = 0; //新节点的所分配的级
  182.     int i = 0;
  183.     //从头节点开始扫描所有级
  184.     for (i = head->GetLevel(); i >= 0; i--)
  185.     {
  186.         next = elemnet->GetElement(i);
  187.         while (next != NULL && _key > next->GetKey())
  188.         {
  189.             elemnet = next;
  190.             next = elemnet->GetElement(i);
  191.         }
  192.         //保存每一级指针
  193.         update.SetElement(i, elemnet);
  194.     }
  195.     elemnet = elemnet->GetElement(0);
  196.     //若键为_key的节点存在跳表中,更新其值
  197.     if ((elemnet != NULL) && (_key == elemnet->GetKey()))
  198.     {
  199.         elemnet->SetValue(_value);
  200.     }
  201.     //不存在键为_key的节点
  202.     else
  203.     {
  204.         //产生一个级
  205.         newLevel = GetNewLevel();
  206.         //若新节点的级比头节点的级大,更新头节点的级
  207.         if (newLevel > head->GetLevel())
  208.         {
  209.             for (int i = head->GetLevel() + 1; i <= newLevel; i++)
  210.             {
  211.                 update.SetElement(i, head);
  212.             }
  213.             head->SetLevel(newLevel);
  214.         }
  215.         //构造新的节点
  216.         elemnet = new SkipListElement(newLevel, _key, _value);
  217.         //调整新节点每一级的指针,以及该节点前后的指针关系
  218.         for (i = 0; i <= newLevel; i++)
  219.         {
  220.             elemnet->SetElement(i, update.GetElement(i)->GetElement(i));
  221.             update.GetElement(i)->SetElement(i, elemnet);
  222.         }
  223.     }
  224. }
  225. /* 从跳表中寻找键为_key的节点的值 */
  226. Value SkipList::Search(const Key _key)
  227. {
  228.     SkipListElement *elemnet = head;
  229.     SkipListElement *next = NULL;
  230.     for (int i = head->GetLevel(); i >= 0; i--)
  231.     {
  232.         next = elemnet->GetElement(i);
  233.         while ((next != NULL) && (_key > next->GetKey()))
  234.         {
  235.             elemnet = next;
  236.             next = elemnet->GetElement(i);
  237.         }
  238.         //存在要找的节点,停止搜索并立即返回
  239.         if ((next != NULL) && (_key == next->GetKey()))
  240.         {
  241.             return next->GetValue();
  242.         }
  243.     }
  244.     //不存在要找的节点
  245.     return SKIPLIST_NOT_FOUND;
  246. }
  247. /* 从跳表中删除键值为_key的节点 */
  248. void SkipList::Free(Key _key)
  249. {
  250.     SkipListElement *element = head;
  251.     SkipListElement *next = NULL;
  252.     SkipListElement temp(MAXLEVEL); //保存欲删除节点前后的指针关系
  253.     int i = 0;
  254.     //从头节点开始扫描所有级
  255.     for (i = head->GetLevel(); i >= 0; i--)
  256.     {
  257.         next = element->GetElement(i);
  258.         //若_key > next->GetKey(),则转入下一个节点扫描剩下的级
  259.         while ((next != NULL) && (_key > next->GetKey()))
  260.         {
  261.             element = next;
  262.             next = next->GetElement(i);
  263.         }
  264.         //temp保存每一级中,欲删除节点的前一个节点指针
  265.         temp.SetElement(i, element);
  266.     }
  267.     element = element->GetElement(0);
  268.     //找到节点
  269.     if (element != NULL && element->GetKey() == _key)
  270.     {
  271.         for (i = 0; i <= element->GetLevel(); i++)
  272.         {
  273.             //修改节点间的指针关系
  274.             temp.GetElement(i)->SetElement(i, element->GetElement(i));
  275.         }
  276.         //删除本节点
  277.         delete element;
  278.         //修改头节点的级,若头节点中某一级的指针为空,则减一级
  279.         while ((head->GetLevel() > 0) && (head->GetElement(head->GetLevel()) == NULL))
  280.         {
  281.             head->SetLevel(head->GetLevel() - 1);
  282.         }
  283.     }
  284. }
  285. /*  输出跳表,形式如下:每一行第一个是头节点(键是一个很大的数值)
  286.     2147483647-->10-->NULL
  287.     2147483647-->10-->20-->NULL
  288.     2147483647-->10-->15-->20-->30-->NULL
  289.     2147483647-->10-->15-->20-->25-->30-->NULL
  290.     2147483647-->10-->15-->20-->25-->30-->NULL
  291. */
  292. ostream& operator<<(ostream outconst SkipList &slist)
  293. {
  294.     SkipListElement *element = slist.head;      
  295.     for (int i = slist.head->GetLevel(); i > 0; i--)
  296.     {
  297.         element = slist.head;
  298.         while (element != NULL)
  299.         {
  300.             cout << element->GetKey() << "-->";
  301.             element = element->GetElement(i);
  302.         }
  303.         cout << "NULL" << endl;
  304.     }
  305.     return out;
  306. }
跳表是一种数据结构,可以高效地实现有序表和有序集合,可以用来实现排行榜。跳表是通过在原始表中添加多索引节点来加速查找操作的。 跳表实现思路如下: 1. 创建一个带有头节点的表,头节点的值为负无穷大,尾节点的值为正无穷大。 2. 在原始表中,插入新的节点时,根据节点的值,决定是否在当前层上添加索引节点。添加索引节点的概率可以根据需求进行调整。 3. 使用索引节点可以将跳表分为多个层(level),每一层都是一个有序表。 4. 查询操作时,从最高层开始,从左向右逐层搜索,直到找到目标值所在的区间(比目标值大的最小节点和比目标值小的最大节点之间)。 5. 对于插入和删除操作,首先在最底层进行,然后根据概率决定是否在上层插入或删除对应的节点。 使用跳表实现排行榜的步骤如下: 1. 创建一个跳表,每个节点存储着用户的信息,包括用户的排名、分数等。 2. 初始化排行榜时,将所有用户按照分数从大到小顺序插入跳表中。 3. 当有新的用户加入或者用户的分数发生变化时,根据新的分数更新用户节点的位置。 4. 当需要查询某个用户的排名时,可以通过跳表中的索引节点,快速定位到该用户所在的层,然后在该层中按照顺序遍历找到目标节点,并返回排名。 通过以上步骤,我们可以使用跳表高效地实现排行榜功能。跳表的插入、删除和查找操作的时间复杂度都可以达到O(log n),在大数据量下具有较高的效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值