跳表数据结构的C++实现

本文介绍了一种通过在有序链表上构建逻辑跳表的数据结构,以提高查询效率。代码实现了跳表的插入、查找和调整操作,适用于频繁插入和更新的应用场景。

1、背景

    有序链表是一种常用的数据结构,具备链表的优点,不会占用较大的连续内存并且可以实现快速插入,适合频繁插入、更新的应用场景。但是随着节点数目的增多,O(n)的查询复杂度使得性能下降。为了优化查询性能,在原有的数据链表基础上,增加一层至多层逻辑链表,这样的数据结构就成为跳表,可以大大加速有序链表的查询性能。

2、代码实现

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

using namespace std;

struct LinkNode

{

    uint64_t key;

    uint64_t value;

    LinkNode* next = nullptr;

};



struct SkipLinkNode

{

    uint64_t key;

    int count;

    LinkNode* pointer = nullptr;

    SkipLinkNode* next = nullptr;

};



class SkipLinkList

{
public:

    SkipLinkList(int skipStep = 128)

        : _skipStep(skipStep)

    {

    }



    ~SkipLinkList()

    {

        LinkNode* node = _listHeader;

        while (node != nullptr)

        {

            LinkNode* toDelNode = node;

            node = node->next;

            delete toDelNode;

        }



        SkipLinkNode* skipNode = _skipListHeader;

        while (skipNode != nullptr)

        {

            SkipLinkNode* toDelNode = skipNode;

            skipNode = skipNode->next;

            delete toDelNode;

        }

    }



public:

    void insert(uint64_t key, uint64_t value)

    {
        // fisrt insert ?
        if (_listHeader == nullptr)
        {
            _listHeader = new LinkNode;
            _listHeader->key = key;
            _listHeader->value = value;
            _listHeader->next = nullptr;

            _skipListHeader = new SkipLinkNode;
            _skipListHeader->count = 1;
            _skipListHeader->key = key;
            _skipListHeader->next = nullptr;
            _skipListHeader->pointer = _listHeader;

            return;
        }

        // exist key?
        LinkNode *pFindNode = find_i(key);
        if (pFindNode)
        {
            pFindNode->value = value;
            return ;
        }

        // key not exist, insert
        LinkNode *pToIstNode = new LinkNode;
        pToIstNode->key = key;
        pToIstNode->value = value;
        pToIstNode->next = NULL;

        // find lower skipNode
        SkipLinkNode* pSkipNode = findLowerSkipNode(key);

        // cannot find, must insert into head;
        if (pSkipNode == nullptr)
        {
            pToIstNode->next = _listHeader;
            _listHeader = pToIstNode;

            _skipListHeader->pointer = _listHeader;
            _skipListHeader->key = _listHeader->key;
            _skipListHeader->count += 1;

            pSkipNode = _skipListHeader;
        }
        else
        {
            LinkNode* pLowerLinkNode = findLowerLinkNode(pSkipNode->pointer, key);
            pToIstNode->next = pLowerLinkNode->next;
            pLowerLinkNode->next = pToIstNode;

            pSkipNode->count += 1;
        }

        adjustSkipNode(pSkipNode);

        return;
    }



    bool find(uint64_t key, uint64_t &value)

    {
       LinkNode *pFindNode = find_i(key);
       if (pFindNode == nullptr)
           return false;

       value = pFindNode->value;

       return true;
    }

    void dump()
    {
        int iNum = 0;
        cout << "SkipNode:" << endl;

        SkipLinkNode *pSkipNode = _skipListHeader;
        while (pSkipNode)
        {
            cout << iNum++ << ": " << pSkipNode->key << ", " << pSkipNode->count << endl;
            pSkipNode = pSkipNode->next;
        }
        cout << endl;

        iNum = 0;
        cout << "LinkNode:" << endl;
        LinkNode *pLinkNode = _listHeader;
        while (pLinkNode)
        {
            cout << iNum++ << ": " << pLinkNode->key << ", " << pLinkNode->value << "; ";
            pLinkNode = pLinkNode->next;
        }
        cout << endl << endl;
    }

private:

    LinkNode* find_i(uint64_t key)
    {
        SkipLinkNode *pSkipNode = findLowerSkipNode(key);
        if (pSkipNode == nullptr)
            return nullptr;

        LinkNode *pLinkNode = pSkipNode->pointer;
        while (pLinkNode && pLinkNode->key < key)
        {
            pLinkNode = pLinkNode->next;
        }

        return (pLinkNode && pLinkNode->key == key) ? pLinkNode : nullptr;
    }

    SkipLinkNode* findLowerSkipNode(uint64_t key)
    {
        if (_skipListHeader == nullptr)
            return nullptr;

        if (key < _skipListHeader->key)
            return nullptr;
        else if (key == _skipListHeader->key)
            return _skipListHeader;

        SkipLinkNode* pPrev = _skipListHeader;
        SkipLinkNode* pCur = pPrev->next;
        while (pCur && pCur->key <= key)
        {
            pPrev = pCur;
            pCur = pCur->next;
        }

        return pPrev;
    }

    LinkNode* findLowerLinkNode(LinkNode* pNodeHeader, uint64_t key)
    {
        //assert(pNodeHeader && pNodeHeader->key < key);

        LinkNode *pCur = pNodeHeader->next;
        while (pCur && pCur->key < key)
        {
            pNodeHeader = pCur;
            pCur = pNodeHeader->next;
        }

        return pNodeHeader;
    }

    void adjustSkipNode(SkipLinkNode *pSkipNode)
    {
        // no need adjust, return
        if (pSkipNode == nullptr || pSkipNode->count <= _skipStep)
            return;

        LinkNode* pLinkNode = pSkipNode->pointer;
        int iCount = 1;
        while (iCount <= _skipStep)
        {
            pLinkNode = pLinkNode->next;
            ++iCount;
        }

        int iExtraCount = pSkipNode->count - _skipStep;
        pSkipNode->count = _skipStep;

        SkipLinkNode *pChgSkipNode = nullptr;
        if (pSkipNode->next == nullptr)
        {
            pChgSkipNode = new SkipLinkNode;
            pChgSkipNode->key = pLinkNode->key;
            pChgSkipNode->count = iExtraCount;
            pChgSkipNode->next = nullptr;
            pChgSkipNode->pointer = pLinkNode;

            pSkipNode->next = pChgSkipNode;
            return ;
        }
        else
        {
            pChgSkipNode = pSkipNode->next;
            pChgSkipNode->key = pLinkNode->key;
            pChgSkipNode->count += iExtraCount;
            pChgSkipNode->pointer = pLinkNode;

            adjustSkipNode(pChgSkipNode);
        }

        return;
    }

    int length()
    {
        int iLen = 0;
        SkipLinkNode *pSkipHeader = _skipListHeader;
        while (pSkipHeader)
        {
            iLen += pSkipHeader->count;
            pSkipHeader = pSkipHeader->next;
        }

        return iLen;
    }

    LinkNode* _listHeader = nullptr;

    SkipLinkNode* _skipListHeader = nullptr;

    int _skipStep;

};





int main()

{

    SkipLinkList list;

    vector<uint64_t>  keys;

    for (uint64_t i = 0;  i < 512; i++)

    {

        keys.push_back(i);

    }

    random_shuffle(keys.begin(), keys.end());



    // insert

    for (auto key : keys)

    {

        list.insert(key, key + 1);

    }

    list.dump();

    for (auto key: keys)

    {

        uint64_t value;

        bool ret = list.find(key, value);

        if (!ret || value != (key + 1))

        {

            cout << "error!" << endl;

            return -1;

        }

    }



    // replace

    list.insert(0,  100);

    uint64_t value;

    bool ret = list.find(0, value);

    if (!ret || value != 100)

    {

        cout << "find replace key error!" << endl;

        return -1;

    }



    // not exist key

    ret = list.find(1000, value);

    if (ret)

    {

        cout << "find not exist error!" << endl;

        return -1;

    }

    list.dump();

    return 0;

}

3、代码分析

    上述代码主要实现了拥有一层逻辑跳表的插入、查询与更新的升序列表功能,其中skipStep表示跳表节点可覆盖数据节点的最大个数。

   一、查询算法

    1、遍历逻辑跳表,找到最后一个skipNode,满足skipNode->key <= key

    2、如果找到满足条件的skipNode,进入步骤三;否则返回未找到

    3、从skipNode指向的linkNode开始向后遍历,直到linkNode==NULL 或者 linkNode->key >= key为止

    4、判断linkNode->key是否等于key,如果等于,查找到数据节点,返回值;否则指定的key不存在此列表中。

    二、插入算法

    1、遍历逻辑跳表,找到最后一个skipNode,满足skipNode->key <= key

    2、如果找到满足条件的skipNode,进入步骤三;否则说明列表中不存在插入元素key小的数据节点,直接在数据链表的头部插入,同时修改跳表头节点的覆盖数据节点个数加1,进入步骤5

    3、从skipNode指向的linkNode开始向后遍历,直到linkNode==NULL 或者 linkNode->key > key为止,记录linkNode的前驱节点为pPrev

    4、如果pPrev的key等于插入元素的key,替换值, 返回;否则,在pPrev节点后插入元素,将skipNode节点的覆盖数据节点个数加1,进入步骤5

     5、判断当前skipNode所覆盖的数据节点个数是否大于skipStep,如果不大于,直接返回;否则,进入步骤6

     6、从skipNode指向的数据节点linkNode开始,向后遍历skipStep个节点nextLinkNode,修改skipNode覆盖数据节点的个数为skipStep

     7、判断skipNode节点的next是否为空,如果为空,在skipNode后插入一个新的跳表节点,设置key为nextLinkNode的key,覆盖数据节点为1,返回;否则进入步骤8

     8、修改skipNode节点的后继节点,将其数据节点指针指向nextLinkNode,其覆盖的数据节点加1,然后将其作为当前skipNode,进入步骤5

    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值