字典的跳表描述

在跳表结构中有一组等级链表,0级链表包含所有数对,一级链表的数对是0级链表数对的一个子集。i级链表的数对是i-1级链表数对的子集。通过这种方式允许我们能够对链表进行折半查找。
对于跳表而言最难的是当插入数据时,该数据到底属于哪级指针,一般是按照一定的概率随机分配。

template<class K,class E>
struct skipNode
{
    typedef pair<const K,E> pairType;
    pairType element;
    skipNode<K,E> **next;//用来存储多级链表

    skipNode(const pairType& thePair,int size):element(thePair)
    {
        next=new skipNode<K,E>*[size];//给其分配size个内存
    }
}

float cutOff;//用来确定层数
int levels;//当前最大的非空链表
int dSize;//字典的数对个数
int maxLevel;//允许的最大链表层数
K tailKey;//最大关键字
skipNode<K,E>* headerNode;//头节点指针
skipNode<K,E>* tailNode;//尾节点指针
skipNode<K,E>** last;//表示i层的最后节点

template<class K,class E>
skipList<K,E>::skipList(K largeKey,int maxPairs,float prob)
{//构造函数,关键字小于largeKey且数对个数最多为maxPairs.0<prob<1是随机数对级数的概率
cutOff=prob*RAND_MAX;
maxLevel=(int)ceil(logf((float)maxPairs)/logf(1/prob))-1;//最大层数计算公式:log(1/f)(N)其中1/f为下标,N为上标
levels=0//当前最大层级
dSize=0;//当前数对个数
tailKey=largeKey;//尾键等于最大键值
//生成头节点,尾节点和数组last
pair<K,E> tailPair;
tailPair.first=tailKey;
headerNode=new skipNode<K,E>(tailPair,maxLevel+1);//头节点里面存储的是tailPair,和各级链表的头地址。
tailNode=new skipNode<K,E>(tailPair,0); 
last=new skipNode<K,E>*[maxLevel+1];//在插入和删除之前的搜索时所遇到的美肌链表的最后一个数对指针都存在数组 last中
//链表为空时,任意链表中的头节点都指向尾节点
for(int i=0;i<=maxLevel;i++)
    headerNode->next[i]=tailNode;
}
template<class K,class E>
int skipList<K,E>::level() const
{//返回一个表示链表级的随机数,这个数不大于maxLevel
int lev=0;
while(rand()<=cutOff)//一次随机相当于p,两次相当于p^2,三次相当于p^3...
    lev++;//随机一次小于p为第一层,两次都是p第二层,三次第三层,越高概率越小,用此来决定当前层数。
return (lev<=maxLevel)?lev:maxLevel;//规定最大层不能超过规定的最大层,因为100层的概率也是存在的。   
}

template<class K,class E>
void skipList<K,E>::insert(const pair<const K,E>& thePair)
{//把数对thePair插入字典,覆盖其关键字相同的已存在的数对
if(thePair.first>=tailKey)
    ostringstream s;
s<<"too large";
throw illegalParameterValue(s.str());
//查看关键字为theKey的数对是否已经存在
skipNode<K,E>* theNode=search(thePair.first);
if(theNode->element.first==thePair.first)
{//若存在,则更新该数对的值
    theNode->element.second=thePair.second;
    return;
}
//若不存在则确定新节点所在的级链表
int theLevel=level();
if(theLevel>levels)
{
    theLevel=++levels;//就算theLevel非常大但是也只能在当前最大级上加1.
    last[theLevel]=headerNode;//新级链表之前的数据就是headerNode因为他是新加入的,必须从headerNode开始连接 
}
//在节点theNode之前插入新节点
skipNode<K,E>* newNode=new skipNode<K,E>(thePair,theLevel+1)//新节点的指针域必须大于当前自身级数
for(int i=0;i<=theLevel;i++)
{//插入i级链表
newNode->next[i]=last[i]->next[i]//第theLevel级节点为新加入的所以其next会指向尾节点
last[i]->next[i]=newNode;        //而其他级别的last[i]则指向其前一个节点的各级链表指针。
}//以上两句就是一个简单的链表插入语句
dSize++;
return;
}

template<class K,class E>
pair<const K,E>* skipList<K,E>::find(const K& theKey)const
{//返回匹配的数对的指针,如果没有则返回NULL
if(theKey>=tailKey)
    return NULL;
//位置beforeNode是关键字为theKey的节点之前最右边的位置
skipNode<K,E>*beforeNode=headerNode;
for(int i=levels;i>=0;i--)//从上级链表到下级链表
//跟踪i级链表指针
while (beforeNode->next[i]->element.first<theKey)
    beforeNode=beforeNode->next[i];//每个节点里面都存储了该节点拥有的最高级链表以下的各级链表节点
if(beforeNode->next[0]->element.first==theKey)
    return &beforeNode->next[0]->element;
return NULL;    
}

template<class K,class E>
skipNode<K,E>* skipList<K,E>::search(const K&theKey)const
{//搜索关键字theKey,把每一级链表中要查看的最后一个节点存储在数组last中
//返回包含关键字theKey的节点
//位子beforeNode是关键字为theKey的节点之前最右边的位置
skipNode<K,E>* beforeNode=headerNode;
for(int i=levels;i>=0;i--)
{
    while(beforeNode->next[i]->element.first<theKey)
        beforeNode=beforeNode->next[i];
    last[i]=beforeNode;//把每一级的前一个节点放入last数组方便之后的插入和删除
}
return beforeNode->next[0]; 
}

template<class K,class E>
void skipList<K,E>::erase(const K& theKey)
{//删除关键字等于theKey的数对
if(theKey>=tailKey)
    return;
//查看是否有匹配的数对
skipNode<K,E>*theNode=search(theKey);
if(theNode->element.first!=theKey)
    return;
for(int i=0;i<=levels&&last[i]->next[i]==theNode;i++)//只有当该节点属于该级时才对其动刀。
    last[i]->next[i]=theNode->next[i];
//更新链表级
while(levels>0&&headerNode->next[levels]==tailNode)
    levels--;
delete theNode;
dSize--;
}

上面代码很难理解,但是理解后就是一句话。
每个节点里面存一个数据element和一个二级指针next,next[0]代表0级链表指向下一个节点,next[1]代表1级链表指向更远的一个节点,next[i]指向更更远的节点,该节点最高有几级链表,next数组就有多大。

真的看得头晕,但还是得坚持啊。

猜你喜欢

转载自blog.csdn.net/du_shuang/article/details/81203615