数据结构:跳表

1、理想情况

在一个使用有序链表描述的具有n个元素的字典中进行搜索,至多需要n次比较。如果在链中部节点加一个指针,则比较次数可以减少到n/2+1。搜索时,首先将要搜索的元素与中间节点进行比较,如果该元素较小,则仅需搜索链表的左半部分。否则,只需搜索又半部分。

以上图为例,如果要搜索的数为26,则将26先与40比较,因为26<40,因此只需要搜索40的左边元素。

而如果在左半部分和右半部分再增加一个中间指针,则可以进一步减小搜索范围(b)。

初始的链称为0级链,如上图中的全部节点。

至少指向2个节点的链称为1级链,上图e中的24,40,75,77

以此类推,这种结构就称为跳表。i级链所包含的元素是i-1级链的子集。

2、插入和删除

  在进行插入和删除时,要想保持跳表的结构,必须耗时O(n)。在这种结构中,有n/2i个元素为i级链元素,在进行插入式应尽量逼近这种结构。在进行插入时,新元素属于i级链的概率为1/2i.在确定新元素的级时,应考虑各种可能的情况。因此,把新元素作为i级链的可能性为pi。对于一般的p,链的级数为log1/pn+1。在这种情况下,每p个i-1级链中有一个在i级链中。插入时,首先要通过搜索以确定链中是否有此元素,搜索时记录下每一层遇到的最后一个元素。插入时,要为新元素分配一个级,分配过程由随机数产生器决定。

删除时,也要先通过搜索确定元素是否在链中以及每一层遇到的最后一个元素。

3、级分配

  如前面分析所述,i-1级链中的元素属于i级链的概率为p。假设有一个随机数产生器所产生的数在0到RAND_MAX之间,则下一次所产生的属技术小于等于CutOff=p*RAND_MAX的概率为p。因此,若下一个随机数小于等于CutOff,则新元素应在1级链上。是否在2级链上,则由下一个随机数决定。重复这个过程,直到得到一个随机数大于CutOff为止。

int lev=0

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

这种方法的潜在缺点是可能为某些元素分配特别大的级,从而导致一些元素的级远超过log1/pN,其中N为字典中预期的最大数目。

为避免这种情况,可以设定一个上限Maxlevel。在有N个元素的跳表中,最大值为log1/pN-1。

另一个缺点是还可能出现这种情况:如在插入一个新元素前有三条链,而在插入之后就有了10条链。这是,新插入的元素为9级,尽管前面并没有出现3到8级元素。

这种情况可以通过将新元素的级调整为3。

if(lev>Levels)

lev=++Levels;

Levels为当前的最大链级

 1 template<typename K,typename E>
 2 int SkipList<K,E>::Level()
 3 {
 4     int lev = 0;
 5     while (rand()<=CutOff)
 6     {
 7         ++lev;
 8     }
 9
10     return (lev <= MaxLevel) ? lev : MaxLevel;
11 }

4、实现

节点:

 1 template<typename K,typename E>
 2 class SkipNode
 3 {
 4     friend ostream& operator<< <>(ostream&, const SkipNode<K, E>&);
 5     friend class SkipList < K, E >;
 6 public:
 7     SkipNode(int size,const K& k=0,const E& e=0):key(k),data(e)
 8     {
 9         link = new SkipNode<K, E>*[size];
10         for (int i = 0; i < size;++i)
11         {
12             link[i] = NULL;
13         }
14
15     }
16     ~SkipNode()
17     {
18         if (link!=NULL)
19         {
20             delete[] link;
21         }
22
23         link = NULL;
24     }
25     //获取0级连接
26     SkipNode<K,E>* getLinkLevel0()const
27     {
28         if (link!=NULL&&link[0]!=NULL)
29         {
30             return link[0];
31         }
32         return NULL;
33     }
34 private:
35     E data;
36     K key;
37     SkipNode<K, E>** link;
38 };
39
40 template<typename K,typename E>
41 ostream& operator<<(ostream& output,const SkipNode<K,E>& s)
42 {
43     if (&s!=NULL)
44     {
45         output << s.key << "=>" << s.data << endl;
46     }
47
48     return output;
49 }

完整实现:

  1 #ifndef SKIPLIST_H
  2 #define SKIPLIST_H
  3
  4 #include<iostream>
  5 #include<cstdlib>
  6 #include<time.h>
  7 using namespace std;
  8 template<typename K,typename E>
  9 class SkipList;
 10
 11 template<typename K,typename E>
 12 class SkipNode
 13 {
 14     friend ostream& operator<< <>(ostream&, const SkipNode<K, E>&);
 15     friend class SkipList < K, E >;
 16 public:
 17     SkipNode(int size,const K& k=0,const E& e=0):key(k),data(e)
 18     {
 19         link = new SkipNode<K, E>*[size];
 20         for (int i = 0; i < size;++i)
 21         {
 22             link[i] = NULL;
 23         }
 24
 25     }
 26     ~SkipNode()
 27     {
 28         if (link!=NULL)
 29         {
 30             delete[] link;
 31         }
 32
 33         link = NULL;
 34     }
 35     //获取0级连接
 36     SkipNode<K,E>* getLinkLevel0()const
 37     {
 38         if (link!=NULL&&link[0]!=NULL)
 39         {
 40             return link[0];
 41         }
 42         return NULL;
 43     }
 44 private:
 45     E data;
 46     K key;
 47     SkipNode<K, E>** link;
 48 };
 49
 50 template<typename K,typename E>
 51 ostream& operator<<(ostream& output,const SkipNode<K,E>& s)
 52 {
 53     if (&s!=NULL)
 54     {
 55         output << s.key << "=>" << s.data << endl;
 56     }
 57
 58     return output;
 59 }
 60
 61 template<typename K,typename E>
 62 class SkipList
 63 {
 64     friend ostream& operator<< <>(ostream& output, const SkipList<K, E>& sl);
 65 public:
 66     SkipList(K Large, int MaxE = 10000, float p = 0.5);
 67     ~SkipList();
 68
 69     bool Search(const K&, E&)const;
 70     SkipList<K, E>& Insert(const K&,const E&);
 71     SkipList<K, E>& Delete(const K&, E& e);
 72 private:
 73     int Level();//分配层号
 74     SkipNode<K, E>* SaveSearch(const K&)const;
 75     int MaxLevel;
 76     int Levels;//非空的最大层
 77     int CutOff;//分层阈值
 78     K TailKey;//一个很大的Key值
 79     SkipNode<K, E>* head;
 80     SkipNode<K, E>* tail;
 81     SkipNode<K, E>** last;//记录插入和删除操作时,所遇到的每条链上的最后一个元素
 82 };
 83
 84 template<typename K,typename E>
 85 SkipList<K,E>::SkipList(K Large, int MaxE, float p):Levels(0),TailKey(Large)
 86 {
 87     CutOff = p*RAND_MAX;
 88     MaxLevel = ceil(log(MaxE) / log(1 / p)) - 1;
 89     srand(time(0));
 90
 91     head = new SkipNode<K, E>(MaxLevel+1);
 92     tail = new SkipNode<K, E>(0);
 93     last = new SkipNode<K, E>*[MaxLevel + 1];
 94     tail->key = TailKey;
 95
 96     for (int i = 0; i <= MaxLevel;++i)
 97     {
 98         head->link[i] = tail;
 99     }
100 }
101
102 template<typename K,typename E>
103 SkipList<K,E>::~SkipList()
104 {
105     SkipNode<K, E>* next = NULL;
106     while (head!=tail&&head!=NULL)
107     {
108         next = head->getLinkLevel0();
109         delete head;
110         head = next;
111     }
112
113     delete tail;
114
115     delete[] last;
116 }
117
118 template<typename K,typename E>
119 bool SkipList<K,E>::Search(const K& k, E& e)const
120 {
121     if (k>=TailKey)
122     {
123         return false;
124     }
125
126     SkipNode<K, E>* p = head;
127     for (int i = Levels; i >= 0;--i)//逐级向下搜索,尽可能逼近k
128     {
129         while (p->link[i]!=NULL&&p->link[i]->key<k)
130         {
131             p = p->link[i];
132         }
133     }
134
135     e = p->link[0]->data;
136     return (p->link[0]->key==k);
137 }
138
139 template<typename K,typename E>
140 SkipNode<K,E>* SkipList<K,E>::SaveSearch(const K& k)const
141 {
142
143     SkipNode<K, E>* p = head;
144     for (int i = Levels; i >= 0;--i)
145     {
146         while (p->link[i] != NULL&&p->link[i]->key<k)
147         {
148             p = p->link[i];
149         }
150         last[i] = p;
151     }
152
153     return (p->link[0]);
154 }
155
156 template<typename K,typename E>
157 int SkipList<K,E>::Level()
158 {
159     int lev = 0;
160     while (rand()<=CutOff)
161     {
162         ++lev;
163     }
164
165     return (lev <= MaxLevel) ? lev : MaxLevel;
166 }
167
168 template<typename K,typename E>
169 SkipList<K,E>& SkipList<K,E>::Insert(const K& k,const E& e)
170 {
171     if (k>=TailKey)
172     {
173         std::cerr << "键值过大" << std::endl;
174         //exit(1);
175         return *this;
176     }
177
178     SkipNode<K, E>* p = SaveSearch(k);
179     if (p->key==e)
180     {
181         std::cerr << "当前key已存在" << std::endl;
182         //exit(1);
183         return *this;
184     }
185
186     int lev = Level();
187     if (lev>Levels)
188     {
189         lev = ++Levels;
190         last[lev] = head;
191     }
192
193     SkipNode<K, E>* y = new SkipNode<K, E>(lev+1);
194     y->data = e;
195     y->key = k;
196     for (int i = 0; i <= lev;++i)
197     {
198         y->link[i] = last[i]->link[i];
199         last[i]->link[i] = y;
200     }
201
202     return *this;
203 }
204
205 template<typename K,typename E>
206 SkipList<K,E>& SkipList<K,E>::Delete(const K& k, E& e)
207 {
208     if (k>=TailKey)
209     {
210         std::cerr << "key过大" << std::endl;
211         //exit(1);
212         return *this;
213     }
214
215     SkipNode<K, E>* p = SaveSearch(k);
216     if (p->key!=k)
217     {
218         std::cerr << "key不存在" << std::endl;
219         //exit(1);
220         return *this;
221     }
222
223     for (int i = 0; i <= Levels&&last[i]->link[i] == p;++i)
224     {
225         last[i]->link[i] = p->link[i];
226     }
227
228     //修改级数
229     while (Levels>0&&head->link[Levels]==tail)
230     {
231         --Levels;
232     }
233
234     e = p->data;
235     delete p;
236
237     return *this;
238 }
239
240 template<typename K,typename E>
241 ostream& operator<<(ostream& output,const SkipList<K,E>& sl)
242 {
243     SkipNode<K, E>* p = sl.head->getLinkLevel0();
244     while (p!=sl.tail&&p!=NULL)
245     {
246         output << *p;
247         p = p->getLinkLevel0();
248     }
249
250     return output;
251 }
252 #endif

test.cpp:

 1 #include<iostream>
 2
 3 #include "SkipList.h"
 4
 5 using namespace std;
 6
 7 int main()
 8 {
 9     SkipList<int, char> sl(1000);
10     sl.Insert(0, ‘a‘);
11     sl.Insert(1, ‘b‘);
12     sl.Insert(4, ‘e‘);
13     sl.Insert(3, ‘c‘);
14
15     cout << sl;
16     char x;
17     sl.Delete(0, x);
18     cout << "after delete:" << endl;
19     cout << sl;
20     if (sl.Search(1, x))
21     {
22         cout << "data of key " << 1 << " is " << x << endl;
23     }
24     return 0;
25 }

时间复杂度分析:

构造函数的时间复杂度为O(MaxLevel)

时间: 2024-12-18 15:51:40

数据结构:跳表的相关文章

存储系统的基本数据结构之一: 跳表 (SkipList)

在接下来的系列文章中,我们将介绍一系列应用于存储以及IO子系统的数据结构.这些数据结构相互关联又有着巨大的区别,希望我们能够不辱使命的将他们分门别类的介绍清楚.本文为第一节,介绍一个简单而又有用的数据结构:跳表 (SkipList) 在对跳表进行讨论之前,我们首先描述一下跳表的核心思想. 跳表(Skip List)是有序线性链表的一种.通常对线性链表进行查找需要遍历,因而不能很好的使用二分查找这样快速的方法(想像一下在链表中定位中间元素的复杂度).为了提高查找速率,我们可以将这些线性链表打散,组

红黑树、B(+)树、跳表、AVL等数据结构,应用场景及分析,以及一些英文缩写

在网上学习了一些材料. 这一篇:https://www.zhihu.com/question/30527705 AVL树:最早的平衡二叉树之一.应用相对其他数据结构比较少.windows对进程地址空间的管理用到了AVL树 红黑树:平衡二叉树,广泛用在C++的STL中.map和set都是用红黑树实现的.我们熟悉的STL的map容器底层是RBtree,当然指的不是unordered_map,后者是hash. B/B+树用在磁盘文件组织 数据索引和数据库索引 Trie树 字典树,用在统计和排序大量字符

HBase内存结构之跳表数据结构浅析

最近学习HBase源码时发现HRegion在sotre管理上用到了跳表数据结构ConcurrentSkipListMap: ConcurrentSkipListMap有几个ConcurrentHashMap不能比拟优点: 1.ConcurrentSkipListMap的key是有序的. 2.ConcurrentSkipListMap支持更高的并发. ConcurrentSkipListMap的存取时间是log(N),和线程数几乎无关. 也就是说在数据量一定的情况下,并发的线程越多,Concurr

数据结构和算法之——跳表

之前我们知道,二分查找依赖数组的随机访问,所以只能用数组来实现.如果数据存储在链表中,就真的没法用二分查找了吗?而实际上,我们只需要对链表稍加改造,就可以实现类似"二分"的查找算法,这种改造之后的数据结构叫作跳表(Skip List). 1. 何为跳表? 对于一个单链表,即使链表是有序的,如果我们想要在其中查找某个数据,也只能从头到尾遍历链表,这样效率自然就会很低. 假如我们对链表每两个结点提取一个结点到上一级,然后建立一个索引指向原始结点,如下图所示. 这时候,我们要查找某一个数据的

程序员,你应该知道的数据结构之跳表

跳表的原理 跳表也叫跳跃表,是一种动态的数据结构.如果我们需要在有序链表中进行查找某个值,需要遍历整个链表,二分查找对链表不支持,二分查找的底层要求为数组,遍历整个链表的时间复杂度为O(n).我们可以把链表改造成B树.红黑树.AVL树等数据结构来提升查询效率,但是B树.红黑树.AVL树这些数据结构实现起来非常复杂,里面的细节也比较多.跳表就是为了提升有序链表的查询速度产生的一种动态数据结构,跳表相对B树.红黑树.AVL树这些数据结构实现起来比较简单,但时间复杂度与B树.红黑树.AVL树这些数据结

《数据结构》_8跳表和散列表

跳表 跳表是一个包含n个元素的单链表,且满足以下条件: (1)在单链表的结点中,每隔2i个元素,就增加一个i级指针,0≤i≤?log2n?: (2)其头节点为Head,是一个大小为?log2n?的一维指针数组,里面只存放指向i级的第一个级指针,0≤i≤?log2n?,不存放实际数据元素,它和同i级的指针构成一个存放指针的i级单链表: (3)其尾结点为Tail,是一个可以存放实际数据元素的指针,通常该元素值设为一个较大的数值,作为查找退出的哨兵. 可以借鉴博客https://blog.csdn.n

【搜索引擎(二)】索引、倒排索引、哈希表、跳表

索引 其实在计算机中我们早已接触过跟索引有关的东西,比如数据库里的索引(index),还有硬盘文件系统中其实也有类似的东西,简而言之,索引是一种为了方便找到自己需要的东西而设计出来的条目,你可以通过找索引找到自己想要内容的位置.索引过程是: 关键字->索引->文档.在图书馆内的书分门别类,就是一种按类别来分的索引.当然索引还有很多其他的实现. 仅仅有索引的概念是不够的.虽然分门别类是一种方法,但是我们在拥有一堆文档的时候必须要有从文档到索引的规范过程,并且索引的结构要满足能够让人(或者计算机)

SkipList 跳表

为什么选择跳表 目前经常使用的平衡数据结构有:B树,红黑树,AVL树,Splay Tree, Treep等. 想象一下,给你一张草稿纸,一只笔,一个编辑器,你能立即实现一颗红黑树,或者AVL树 出来吗? 很难吧,这需要时间,要考虑很多细节,要参考一堆算法与数据结构之类的树, 还要参考网上的代码,相当麻烦. 用跳表吧,跳表是一种随机化的数据结构,目前开源软件 Redis 和 LevelDB 都有用到它, 它的效率和红黑树以及 AVL 树不相上下,但跳表的原理相当简单,只要你能熟练操作链表, 就能轻

[考研系列之数据结构]线性表之字符串

基本概念 串(字符串)  由0个或多个字符组成的有限序列,例如s="hello world" 串名  上例中的s 子串  某串任意连续字符组成的子序列,称为此字符串的子串 空串  0个字符的串,s="" 空格串  由一个或多个字符组成的串 模式匹配算法 作用 定位某子串T在字符串S中的位置 主串 S 模式串  T 针对模式匹配算法从简到难我们需要了解两种算法: [1] 朴素的模式匹配算法 [2] KMP匹配算法 朴素的模式匹配算法: 所谓朴素就是简单,这是一种简单的