问题描述

如果用有序链表来表示一个含有n个元素的有序集S,则在最坏情况下,搜索S中一个元素需要O(n)计算时间。提高有序链表效率的一个技巧是在有序链表的部分结点处增设附加指针以提高其搜索性能。在增设附加指针的有序链表中搜索一个元素时,可借助于附加指针跳过链表中若干结点,加快搜索速度。这种增加了向前附加指针的有序链表称为跳跃表。
     应在跳跃表的哪些结点增加附加指针以及在该结点处应增加多少指针完全采用随机化方法来确定。这使得跳跃表可在O(logn)平均时间内支持关于有序集的搜索、插入和删除等运算。

例如:如图,(a)是一个没有附加指针的有序表,而图(b)在图(a)的基础上增加了跳跃一个节点的附加指针。图(c)在图(b)的基础上又增加了跳跃3个节点的附加指针。

在跳跃表中,如果一个节点有k+1个指针,则称此节点为一个k级节点。以图(c)中跳跃表为例,看如何在改跳跃表中搜索元素8。从该跳跃表的最高级,即第2级开始搜索。利用2级指针发现元素8位于节点7和19之间。此时在节点7处降至1级指针进行搜索,发现元素8位于节点7和13之间。最后,在节点7降至0级指针进行搜索,发现元素8位于节点7和11之间,从而知道元素8不在搜索的集合S中。

相关原理

     在一般情况下,给定一个含有n个元素的有序链表,可以将它改造成一个完全跳跃表,使得每一个k级结点含有k+1个指针,分别跳过2^k-1,2^(k-1)-1,…,2^0-1个中间结点。第i个k级结点安排在跳跃表的位置i2^k处,i>=0。这样就可以在时间O(logn)内完成集合成员的搜索运算。在一个完全跳跃表中,最高级的结点是级结点。

完全跳跃表与完全二叉搜索树的情形非常类似。它虽然可以有效地支持成员搜索运算,但不适应于集合动态变化的情况。集合元素的插入和删除运算会破坏完全跳跃表原有的平衡状态,影响后继元素搜索的效率。

为了在动态变化中维持跳跃表中附加指针的平衡性,必须使跳跃表中k级结点数维持在总结点数的一定比例范围内。注意到在一个完全跳跃表中,50%的指针是0级指针;25%的指针是1级指针;…;(100/2^(k+1))%的指针是k级指针。因此,在插入一个元素时,以概率1/2引入一个0级结点,以概率1/4引入一个1级结点,…,以概率1/2^(k+1)引入一个k级结点。另一方面,一个i级结点指向下一个同级或更高级的结点,它所跳过的结点数不再准确地维持在2^i-1。经过这样的修改,就可以在插入或删除一个元素时,通过对跳跃表的局部修改来维持其平衡性。

跳跃表中节点的级别在插入是确定,一旦确定便不再更改。下图是遵循上述原则的跳跃表的例子。对其进行搜索与对完全跳跃表所作的搜索是一样的。如果希望在所示跳跃表中插入一个元素8,则应现在跳跃表中搜索其插入位置。经搜索发现应在节点7和11之间插入元素8.此时在节点7和11之间增加1个存储元素8的新节点,并以随机的方式确定新节点的级别。例如,如果元素8是作为一个2级节点插入,则应对图中虚线相交的指针进行调整,如果新插入的节点是一个1级节点,则只要修改2个指针,虚线相交的指针是有可能被修改的指针,这些指针可在搜索元素插入位置时动态地保存起来,以供插入时使用

在一个完全跳跃表中,具有i级指针的结点中有一半同时具有i+1级指针。为了维持跳跃表的平衡性,可以事先确定一个实数0<p<1,并要求在跳跃表中维持在具有i级指针的结点中同时具有i+1级指针的结点所占比例约为p。为此目的,在插入一个新结点时,先将其结点级别初始化为0,然后用随机数生成器反复地产生一个[0,1]间的随机实数q。如果q<p,则使新结点级别增加1,直至q>=p。由此产生新结点级别的过程可知,所产生的新结点的级别为0的概率为1-p,级别为1的概率为p(1-p),…,级别为i的概率为p^i(1-p)。如此产生的新结点的级别有可能是一个很大的数,甚至远远超过表中元素的个数。为了避免这种情况,用作为新结点级别的上界。其中n是当前跳跃表中结点个数。当前跳跃表中任一结点的级别不超过 

算法具体实现如下:

1、RandomNumber.h

#include"time.h"
//随机数类
const unsigned long maxshort = 65536L;
const unsigned long multiplier = 1194211693L;
const unsigned long adder = 12345L;class RandomNumber
{private://当前种子unsigned long randSeed;public:RandomNumber(unsigned long s = 0);//构造函数,默认值0表示由系统自动产生种子unsigned short Random(unsigned long n);//产生0:n-1之间的随机整数double fRandom(void);//产生[0,1)之间的随机实数
};RandomNumber::RandomNumber(unsigned long s)//产生种子
{if(s == 0){randSeed = time(0);//用系统时间产生种子}else{randSeed = s;//由用户提供种子}
}unsigned short RandomNumber::Random(unsigned long n)//产生0:n-1之间的随机整数
{randSeed = multiplier * randSeed + adder;//线性同余式return (unsigned short)((randSeed>>16)%n);
}double RandomNumber::fRandom(void)//产生[0,1)之间的随机实数
{return Random(maxshort)/double(maxshort);
}

2、7d3d3.cpp

//随机化算法 跳跃表
#include "stdafx.h"
#include "RandomNumber.h"
#include <cmath>
#include <iostream>
using namespace std;template<class EType,class KType> class SkipList;
template<class EType,class KType>
class SkipNode
{friend SkipList<EType,KType>;private:SkipNode(int size){next = new SkipNode<EType,KType>*[size];}~SkipNode(){delete []next;}EType data;//集合中的元素SkipNode<EType,EType> **next;//指针数组 next[i]是第i级指针
};template<class EType,class KType>
class SkipList
{public:SkipList(KType Large,int MaxE = 10000,float p = 0.5);~SkipList();bool Search(const KType& k,EType& e) const;SkipList<EType,KType>& Insert(const EType& e);SkipList<EType,KType>& Delete(const KType& k,EType& e);void Output();private:int Level();SkipNode<EType,KType> *SaveSearch(const KType& k);int MaxLevel;                  //跳跃表级别上界int Levels;                        //当前最大级别RandomNumber rnd;               //随机数产生器float Prob;                     //用于分配节点级别KType TailKey;                    //元素键值上界SkipNode<EType,KType> *head;  //头结点指针SkipNode<EType,KType> *NIL;        //尾节点指针SkipNode<EType,KType> **last;  //指针数组
};//构造函数
template<class EType,class KType>
SkipList<EType,KType>::SkipList(KType Large,int MaxE,float p)
{Prob = p;MaxLevel = ceil(log(float(MaxE))/log(1/p))-1;       //初始化跳跃表级别上界TailKey = Large;                           //元素键值上界Levels = 0;                                    //初始化当前最大级别//创建头、尾节点和数组 lasthead = new SkipNode<EType,KType>(MaxLevel+1);NIL = new SkipNode<EType,KType>(0);last = new SkipNode<EType,KType> *[MaxLevel+1];NIL->data = Large;//将跳跃表初始化为空表for(int i=0; i<=MaxLevel; i++){head->next[i] = NIL;}
}//析构函数
template<class EType,class KType>
SkipList<EType,KType>::~SkipList()
{SkipNode<EType,KType> *next;//删除所有节点while(head!=NIL){next = head->next[0];delete head;head = next;}delete NIL;delete []last;
}class element
{friend int main(void);public:operator long() const {return key;}element& operator = (long y){key = y;return *this;}private:int data;long key;
};//搜索指定元素k
template<class EType,class KType>
bool SkipList<EType,KType>::Search(const KType& k,EType& e) const
{if(k>=TailKey){return false;}//位置p恰好位于指定元素k之前SkipNode<EType,KType> *p = head;for(int i=Levels; i>=0; i--)//逐渐向下搜索{while(p->next[i]->data<k)//在第i级链中搜索{p = p->next[i];//每次搜索尽可能滴接近要搜索的元素}}e = p->next[0]->data;return (e==k);
}//搜索指定元素k,并将每一级中遇到的上一个节点存放在数组last中
template<class EType,class KType>
SkipNode<EType,KType> *SkipList<EType,KType>::SaveSearch(const KType& k)
{//位置p恰好位于指定元素k之前SkipNode<EType,KType> *p = head;for(int i = Levels; i>=0; i--){while(p->next[i]->data<k){p = p->next[i];}last[i] = p;    //上一个第i级结点}return (p->next[0]);
}//产生不超过MaxLevel 的随机级别
template<class EType,class KType>
int SkipList<EType,KType>::Level()
{int lev = 0;while(rnd.fRandom()<Prob){lev++;}return (lev<=MaxLevel)?lev:MaxLevel;
}//插入指定元素e
template<class EType,class KType>
SkipList<EType,KType>& SkipList<EType,KType>::Insert(const EType& e)
{KType k = e;//取得元素键值if(k>=TailKey){cout<<"元素键值超界!"<<endl;return *this;}//检查元素是否已存在SkipNode<EType,KType> *p = SaveSearch(k);if(p->data == e){cout<<"元素已存在!"<<endl;return *this;}//元素不存在,确定新节点级别int lev = Level();//调整个级别指针if(lev>Levels){for(int i=Levels+1; i<=lev; i++){last[i] = head;}Levels = lev;}//产生新节点,并将新节点插入p之后SkipNode<EType,KType> *y = new SkipNode<EType,KType>(lev+1);y->data = e;for(int i=0; i<=lev; i++){//插入第i级链y->next[i] = last[i]->next[i];last[i]->next[i] = y;}return *this;
}//删除键值为k的元素,并将所删除的元素存入e
template<class EType,class KType>
SkipList<EType,KType>& SkipList<EType,KType>::Delete(const KType& k,EType& e)
{if(k>=TailKey){cout<<"元素键值超界!"<<endl;}//搜索待删除元素SkipNode<EType,KType> *p = SaveSearch(k);if(p->data!=k){cout<<"未找到待删除元素!"<<endl;}//从跳跃表中删除节点for(int i=0; i<=Levels && last[i]->next[i] == p; i++){last[i]->next[i] = p->next[i];}//更新当前级别while(Levels>0 && head->next[Levels] == NIL){Levels--;}e = p->data;delete p;return *this;
}//输出集合中的元素
template<class EType,class KType>
void SkipList<EType,KType>::Output()
{SkipNode<EType,KType> *y = head->next[0];for(;y!=NIL; y=y->next[0]){cout<<y->data<<' ';}cout<<endl;
}int main()
{SkipList<int,int> *s = new SkipList<int,int>(100,10,0.5);//创建cout<<"=========创建==========="<<endl;int a[] = {5,3,2,11,7,13,19,17,23};for(int i=0; i<9; i++){s->Insert(a[i]);}s->Output();//搜索cout<<"=========搜索==========="<<endl;int k=17,e;cout<<"搜索元素k="<<k<<"如下:"<<endl;if(s->Search(17,e)){cout<<"位置搜索结果为:k="<<k<<",e="<<e;}cout<<endl;//删除cout<<"=========删除==========="<<endl;cout<<"删除跳跃表元素k="<<k<<"剩余元素如下:"<<endl;s->Delete(k,e);s->Output();delete s;return 0;
}

程序运行结果如图:

0046算法笔记——【随机化算法】舍伍德随机化思想解决跳跃表问题相关推荐

  1. 三个随机算法的简介(舍伍德,拉斯维加斯,蒙特卡洛)

    舍伍德,拉斯维加斯和蒙特卡罗算法 舍伍德算法 拉斯维加斯算法 蒙特卡罗方法 舍伍德算法 很多算法对于不同的输入时间复杂度有较大区别,例如快速排序算法.在这个时候就可以采用舍伍德型概率算法消除这种影响. ...

  2. 数据结构与算法笔记(十五)—— 散列(哈希表)

    一.前沿 1.1.直接寻址表 当关键字的全域U比较小时,直接寻址是一种简单而有效的技术.假设某应用要用到一个动态集合,其中每个元素都有一个取自全域U={0,1,-,m-1)的关键字,此处m是一个不很大 ...

  3. 优化算法笔记|灰狼算法理解及Python实现

    灰狼优化算法的理解和应用 一.背景介绍 二.算法原理 三.构建算法数学模型 四.Python实现GWO 五.算法分析 一.背景介绍 灰狼优化算法(Grey Wolf Optimizer,GWO)由澳大 ...

  4. 算法笔记_070-BellmanFord算法简单介绍(Java)

    目录 1 问题描述 2 解决方案 2.1 具体编码   1 问题描述 何为BellmanFord算法? BellmanFord算法功能:给定一个加权连通图,选取一个顶点,称为起点,求取起点到其它所有顶 ...

  5. 回溯 皇后 算法笔记_算法笔记_04_回溯

    设计思想: (1)适用:求解搜索问题和优化问题. (2)搜索空间:数,节点对应部分解向量,可行解在树叶上. (3)搜索过程:采用系统的方法隐含遍历搜索树. (4)搜索策略:深度优先,宽度优先,函数优先 ...

  6. 算法笔记_163:算法提高 最大乘积(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 对于n个数,从中取出m个数,如何取使得这m个数的乘积最大呢? 输入格式 第一行一个数表示数据组数 每组输入数据共2行: 第1行给出总共的数 ...

  7. 优化算法笔记|萤火虫算法理解及实现

    萤火虫算法 一.萤火虫算法背景知识 二.萤火虫算法 三.萤火虫算法实现 四.算法分析 一.萤火虫算法背景知识 萤火虫算法(Firefly Algorithm, FA)是基于萤火虫的闪光行为,它是一种用 ...

  8. 算法笔记--KMP算法 EXKMP算法

    1.KMP算法 这个博客写的不错:http://www.cnblogs.com/SYCstudio/p/7194315.html 模板: next数组的求解,那个循环本质就是如果相同前后缀不能加上该位 ...

  9. [算法笔记]分块算法从入门到TLE

    分块算法在学习之前一直觉得是一个高端大气上档次,有着与众不同的O(√N)的时间复杂度. (打公式真是太烦了,不过如果我不打公式zichen0535巨佬肯定又要嘲讽我...) 直到我阅读多方博客,才发现 ...

最新文章

  1. c语言产生cl.exe错误,vc++6.0执行cl.exe时出错
  2. unity UV 动画
  3. Abp vNext 二进制大对象系统(BLOB)
  4. linux网络编程之网络字节序、主机字节序、大端、小端
  5. 假如把女生比作一种水果
  6. 基于C++与VS2012的HDF5文件处理(一)
  7. brew安装mysql 卸载_Mac卸载mysql并安装mysql升级到8.0.13版本
  8. R语言学堂开通付费咨询业务了~~
  9. php判断get和post请求,PHP:6种GET和POST请求发送方法
  10. C++头文件预编译与命名空间使用方法
  11. 关闭MySQL日志,删除mysql-bin.0000*日志文件
  12. android qq消息推送消息,最新版手机QQ怎么发送定时消息
  13. Android直播开发之旅(14):使用RC4算法加解密音视频流
  14. Empty suite
  15. 应试教育中的孩子长不好
  16. 鞋之语告诉你如何开好一个新式洗鞋店
  17. 基于at89c52单片机温湿度c语言,基于AT89C52单片机的温度监控报警系统的设计
  18. 远程字典服务Redis,感谢B站黑马程序员,多多三连
  19. 成为视频分析专家:自动生成视频集锦(Python实现)
  20. 杭电OJ第11页2085~2089算法题(C语言)

热门文章

  1. 弹幕炸了!OpenAI直播大秀语音指挥AI自动编程,也就比老罗TNT强亿点点
  2. 看下巴识心情,这个AI项链挂胸前也能识别面部表情
  3. 放张载玻片就能放大一万倍,普通光学显微镜都馋哭了 | Nature子刊
  4. 给卡车穿上“隐身衣”,让自动驾驶车辆撞上它!这场自动驾驶比赛,比谁攻得快...
  5. 人工智能时代,怎样高效关注行业趋势、了解AI技术与落地?
  6. 一份来自亚马逊工程师的Google面试指南,GitHub收获9.8万星,已翻译成中文
  7. Hyperledger Composer评测
  8. Centos6 64位系统安装VNCServer
  9. ios Runloop
  10. android:layout_weight的真实含义