C++ :跳錶資料結構的實現原理
阿新 • • 發佈:2020-12-29
所謂的跳錶,就是在有序連結串列的基礎上加上索引層,在索引層的基礎上還可以再加索引層,從而提高連結串列的查詢效率,一般情況下也可以提高插入和刪除效率。
-----------------------------------------------------------------------
目錄
一、跳錶的資料結構
跳錶由索引層連結串列和資料層連結串列組成,資料層連結串列由 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);
}
}
五、插入和刪除
查詢到要插入的節點位置後,進行普通有序連結串列的插入和刪除即可