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 }