1. 程式人生 > 其它 >C++ :跳錶資料結構的實現原理

C++ :跳錶資料結構的實現原理

技術標籤:C++資料結構C++跳錶連結串列skiplist

所謂的跳錶,就是在有序連結串列的基礎上加上索引層,在索引層的基礎上還可以再加索引層,從而提高連結串列的查詢效率,一般情況下也可以提高插入和刪除效率。

-----------------------------------------------------------------------

目錄

一、跳錶的資料結構

二、重建索引

三、根據位置確定資料節點

四、根據元素data資訊來查詢元素位置

五、插入和刪除


一、跳錶的資料結構

跳錶由索引層連結串列和資料層連結串列組成,資料層連結串列由 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);
    }
}

五、插入和刪除

查詢到要插入的節點位置後,進行普通有序連結串列的插入和刪除即可