所谓的跳表,就是在有序链表的基础上加上索引层,在索引层的基础上还可以再加索引层,从而提高链表的查询效率,一般情况下也可以提高插入和删除效率。
-----------------------------------------------------------------------
目录
一、跳表的数据结构
跳表由索引层链表和数据层链表组成,数据层链表由 next指针部分和数据部分组成。索引层链表由 数据部分、索引指针部分、next指针部分三部分组成,其中数据部分值和索引指针指向节点的值保持一致。最底层的索引指针指向数据层链表节点,其它层索引指针指向低层的索引链表节点。
template<class ElemType>
class SkipList {
public:
class IndexStruct{
public:
ElemType position() const;
void position(ElemType position);
bool operator <(IndexStruct a);
bool operator ==(IndexStruct a);
bool operator >(IndexStruct a);
IndexStruct& operator =(const IndexStruct & a);
IndexStruct(ElemType position, const boost::shared_ptr<LinkList<ElemType>> &pointer);
IndexStruct(){}
public:
struct U{
boost::shared_ptr<LinkList<IndexStruct>> indexPointer;
boost::shared_ptr<LinkList<ElemType>> dataPointer;
}pointer_;
const U &pointer() const;
void pointer(const U &pointer);
ElemType position_; //datalist的索引位
};
explicit SkipList(Integer skipstep = 2):skipstep_(skipstep+1){}
public:
int findIndexListPosition(int indexlevel,ElemType dataPos,boost::shared_ptr<LinkList<IndexStruct>>& idxlist);
void rebuildIndex(ElemType startElem );
int findPosByElem(ElemType elem);
void removeByPos(Size pos);
boost::shared_ptr<LinkList<ElemType>> findNodeByPos(Size pos);
void removeByElem(ElemType e);
void insertElem(ElemType e,bool isRebuildIndex = false);
boost::shared_ptr<LinkList<ElemType>>findDataNode(ElemType elem,bool isAccurate = true);
boost::shared_ptr<LinkList<IndexStruct>> findIndexNode(int indexlevel,ElemType dataPos);
const std::vector<boost::shared_ptr<LinkList<IndexStruct>>> &indexLinklist() const{
return indexLinklist_;
};
void indexLinklist(const std::vector<boost::shared_ptr<LinkList<IndexStruct>>> &indexLinklist){
indexLinklist_=indexLinklist;
};
const boost::shared_ptr<LinkList<ElemType>> &dataLinklist() const{
return dataLinklist_;
};
void dataLinklist(const boost::shared_ptr<LinkList<ElemType>> &dataLinklist){
dataLinklist_=dataLinklist;
if (dataLinklist_ ->next){
rebuildIndex(dataLinklist_->next->data);
}else{
indexLinklist_.clear();
}
};
private:
std::vector< boost::shared_ptr<LinkList<IndexStruct>>> indexLinklist_;//索引链表,数据是地址
boost::shared_ptr<LinkList<ElemType>> dataLinklist_; //有序数据链表
LinkListUtil<ElemType> dataListUtil_;
LinkListUtil<IndexStruct> indexListUtil_;
const Integer skipstep_;
};
二、重建索引
已知有数据层链表dataLinkList ,长度为 n,那么第 level层需要建立的索引数是 datalistsize /pow(skipstep_,level ) 个,每隔skipstep个数据节点就建立一个索引,直到第level层需要建立的节点数是0就不再键索引。在此过程中,如果层级要大于原有索引层级需要扩充索引层,否则再原来索引层进行修改。
/**
* <p>从第数据链表的startElem开始重建立链表
* @tparam ElemType
* @param startElem
*/
template<class ElemType>
void SmartDongLib::SkipList<ElemType>::rebuildIndex(ElemType startElem) {
//获取数据总数
int datalistsize = dataListUtil_.listLenth(dataLinklist_);
//要重建数据索引的个数
// int rebuldDataCount = datalistsize - startIndex +1;
//索引层应建立索引的节点数
int indexlevelCount = datalistsize / skipstep_;
int indexlevel=0;
while (indexlevelCount !=0){
//如果层级要大于 indexLinklist_.size(),需要扩充indexList,否则再原来的基础上修改,还需要判断是不是第0索引层
if(indexlevel >= indexLinklist_.size()){
boost::shared_ptr<LinkList<IndexStruct>> currentIndexList (new LinkList<IndexStruct>());
//头指针也进行关联
if (indexlevel ==0){
currentIndexList->data.pointer_.dataPointer = dataLinklist_;
// currentIndexList->data.position_ =dataLinklist_->data;
}else{
currentIndexList->data.pointer_.indexPointer = indexLinklist_[indexlevel-1];
// currentIndexList->data.position_ =indexLinklist_[indexlevel-1]->data.position_;
}
bool isfirst = true;
boost::shared_ptr<LinkList<ElemType>> linkdataNode;
boost::shared_ptr<LinkList<IndexStruct>> linkindexNode;
//扩展 indexlevel 层的后面的索引
for (int i = 1; i <=indexlevelCount; ++i) {
// boost::shared_ptr<LinkList<IndexStruct>> currentIndexNode (new LinkList<IndexStruct>);
IndexStruct currentIndexNode;
//第0索引层指向data数据,其他指向下层索引数据。优化:第一次从头节点确定指向的位置,之后再此基础上在+上skipstep_
if (indexlevel == 0) {
if (isfirst){
linkdataNode = dataListUtil_.listGetNode(dataLinklist_,i * skipstep_);
isfirst= false;
}else{
linkdataNode = dataListUtil_.listGetNode(linkdataNode, skipstep_);
}
currentIndexNode.position_ =linkdataNode->data;
currentIndexNode.pointer_.dataPointer = linkdataNode;
}else{
if (isfirst){
linkindexNode =indexListUtil_.listGetNode(indexLinklist_[indexlevel - 1], i * skipstep_);
isfirst= false;
}else{
linkindexNode =indexListUtil_.listGetNode(linkindexNode, skipstep_);
}
currentIndexNode.position_ =linkindexNode->data.position_;
currentIndexNode.pointer_.indexPointer = linkindexNode;
}
indexListUtil_.listOrderInsert(currentIndexList,currentIndexNode);
}
indexLinklist_.push_back(currentIndexList);
} else{
//如果在原来的索引层上进行修改,那么确认要修改的索引节点进行重建
boost::shared_ptr<LinkList<IndexStruct>> currentIndexList=indexLinklist_[indexlevel];
//找到startElem前一个元素的位置
boost::shared_ptr<LinkList<IndexStruct>> startIndexNode;
int startIdx = findIndexListPosition(indexlevel, startElem,startIndexNode);
//重键startIndexNode之后的索引
startIndexNode->next = NULL;
boost::shared_ptr<LinkList<ElemType>> linkdataNode;
boost::shared_ptr<LinkList<IndexStruct>> linkindexNode;
bool isfirst = true;
//第indexlevel层从startIdx开始重建索引
for (int i = startIdx+1; i <=indexlevelCount; ++i){
// boost::shared_ptr<LinkList<IndexStruct>> currentIndexNode (new LinkList<IndexStruct>);
// LinkList<IndexStruct> currentIndexNode;
IndexStruct currentIndexNode;
//优化:第0索引层指向data数据,其他指向下层索引数据,第一次从头节点确定指向的位置,之后再此基础上在+上skipstep_
if (indexlevel == 0) {
if (isfirst){
linkdataNode = dataListUtil_.listGetNode(dataLinklist_,i * skipstep_);
isfirst= false;
}else{
linkdataNode = dataListUtil_.listGetNode(linkdataNode, skipstep_);
}
currentIndexNode.position_ =linkdataNode->data;
currentIndexNode.pointer_.dataPointer = linkdataNode;
}else{
if (isfirst){
linkindexNode =indexListUtil_.listGetNode(indexLinklist_[indexlevel - 1], i * skipstep_);
isfirst= false;
}else{
linkindexNode =indexListUtil_.listGetNode(linkindexNode, skipstep_);
}
currentIndexNode.position_ =linkindexNode->data.position_;
currentIndexNode.pointer_.indexPointer = linkindexNode;
}
indexListUtil_.listOrderInsert(currentIndexList,currentIndexNode);
}
}
indexlevel++;
indexlevelCount /= skipstep_;
}
}
三、根据位置确定数据节点
在level层移动一次next 对应的数据层节点数增加pow(skipstep_,currentIndexlevel)位置。
/**
* <p>获取节点(已优化)
* @tparam ElemType
* @param pos data的位置
* @return 获取pos对应的元素节点
*/
template<class ElemType>
boost::shared_ptr<SmartDongLib::LinkList<ElemType>> SmartDongLib::SkipList<ElemType>::findNodeByPos(Size pos) {
int indexlistsize = indexLinklist_.size();
int currentIndexlevel = indexlistsize-1;
boost::shared_ptr<LinkList<IndexStruct>> currentIndexNode = indexLinklist_[currentIndexlevel];
int currentPos = 0;
int posIncrement =1;
boost::shared_ptr<LinkList<ElemType>> ret = dataLinklist_;
while(currentPos <= pos && currentIndexlevel >=0){
posIncrement = std::pow(skipstep_,currentIndexlevel + 1);
if ( currentIndexNode->next!=NULL && currentPos + posIncrement <=pos ){
//如果查在后面
currentIndexNode=currentIndexNode->next;
currentPos +=posIncrement;
}else{
//如果当前层确定了位置,就下一层直到第indexlevel结束
if ( currentIndexlevel == 0 ){
ret = currentIndexNode->data.pointer_.dataPointer;
currentIndexlevel --;
break;
}else{
currentIndexNode = currentIndexNode->data.pointer_.indexPointer;
currentIndexlevel --;
}
}
}
while (pos - currentPos >0 && currentIndexlevel<0){
ret = ret->next;
currentPos ++ ;
}
return ret;
}
四、根据元素data信息来查询元素位置
/**
* <p>根据元素寻找在datalinklist中的位置(已优化)
* @tparam ElemType
* @param elem
* @return 位找到是-1 ,头节点是第0个位置
*/
template<class ElemType>
int SmartDongLib::SkipList<ElemType>::findPosByElem(ElemType elem) {
int indexlistsize = indexLinklist_.size();
int currentIndexlevel = indexlistsize-1;
boost::shared_ptr<LinkList<IndexStruct>> idxlist=indexLinklist_[currentIndexlevel];
int pos = 0;
while (currentIndexlevel >= 0){
if ( idxlist->next && idxlist->next->data.position() <= elem ){
//如果查在后面
idxlist=idxlist->next;
pos =pos + std::pow(skipstep_,currentIndexlevel +1 );
}else{
//如果当前层确定了位置,就下一层直到第indexlevel结束
if ( currentIndexlevel == 0 ){
break;
}else{
idxlist = idxlist->data.pointer_.indexPointer;
currentIndexlevel --;
}
}
}
boost::shared_ptr<LinkList<ElemType>> dataNode = idxlist->data.pointer_.dataPointer;
if (dataNode ->data == elem){
return pos;
} else{
int rslt =dataListUtil_.listGetIndex(dataNode,elem);
if (rslt == -1 )
return -1;
else
return pos + dataListUtil_.listGetIndex(dataNode,elem);
}
}
五、插入和删除
查询到要插入的节点位置后,进行普通有序链表的插入和删除即可