一种伪严格跳表的实现

一种伪严格跳表的实现

一种伪严格跳表的实现

初始化严格跳表

记录nowidx为现在最后一个节点的下标,从0开始标注(0是head),记录bestN为初始化时候的节点数(新建空跳表时候记为10或者小一些),记录ops为成功的删除操作数量(只有删除节点会影响结构),size为节点数,
隔(1/p)^n个节点来设置n级别节点。
比如p=0.5时候。搁2个设置1,级,4个设置2级,8个设置3级。。。
这样严格可在logn次查找内找到要搜索的值。

实现上,在p=0.5时,直接二进制拆位,第一个1的位置就是节点的级数。

//index代表当前节点下标
int indx=index;
int lev = 0;
while (!(1 & indx))
{
	lev++;
	indx >>= 1;
}

间隔重整

删除节点时候只会考虑nowidx,不会考虑前面的结构,一定量删除节点操作后严格会被大幅度破坏。

在ops=bestN时进行重整,把所有节点的值(pair)按顺序导出到数组(可用vector),删除原跳表,新建bestN=size的跳表,导入所有节点的值进行初始化。

复杂度的不严格分析:每bestN次删除操作后重整复杂度为size次的add与size次的erase。考虑到size<=bestN,也就是最坏情况下相当于多进行两倍的操作量,只对会影响复杂度中的常数。

后记

考虑到无论是否严格,刚完成初始化的跳表的平均复杂度均为logn。当数据量稍大一些时便可抵消随机性带来的不稳定。严格跳表时间复杂度更高一些,带来的优点仅仅为erase操作进行前的稳定性。
而另一方面,多次操作后跳表结构总会被破坏,这会带来一定的不稳定性,当稳定性非常重要时,花费一定的时间复杂度去重整链表是可取的,但这与链表是否初始化为严格链表关系并不大。

#include "chrono"
#include "iomanip"
#include "sstream"
#include "string"
#include "time.h"
#include <cmath>
#include <iostream>
using namespace std;
template<typename K, typename E>
struct SkiplistNode {
    pair<K, E> ele;
    SkiplistNode<K, E> **next;
    int lev;
    SkiplistNode(K key = -1, E element = -1, int l = 0)
    {
        ele.first = key;
        ele.second = element;
        next = new SkiplistNode *[l + 1];
        lev = l;
    }
    ~SkiplistNode()
    {
        delete[] next;
    }
};
template<typename K = int, typename E = int>
class Skiplist
{
private:
    double p;
    SkiplistNode<K, E> *head;
    SkiplistNode<K, E> *tail;
    SkiplistNode<K, E> **last;

    int maxkey;
    int nowlevel;
    int maxl;
    int numsize;
    int nowidx;
    int bestN;
    int ops;
    int getL()
    {
        /*int l = 0;
        while (double(rand()) / RAND_MAX <= p)
            l++;
        return min(l, maxl);*/
        int indx = nowidx;
        int lev = 0;
        while (!(1 & indx) && indx)
        {
            lev++;
            indx >>= 1;
        }
        return min(lev, maxl);
    }
    void remake()
    {
        pair<K, E> *dat = new pair<K, E>[numsize];
        auto it = head;
        it = it->next[0];
        int tsize=numsize;
        for (int i = 0; i < tsize; ++i)
        {
            dat[i] = it->ele;
            auto tit = it;
            it = it->next[0];
            erase(tit->ele.second);
        }

        bestN = tsize;
        nowlevel = 0;
        nowidx = 0;
        ops = 0;

        for (int i = 0; i < tsize; ++i)
            add(dat[i].second);
        delete[] dat;
    }
    void check()
    {
        if (ops == bestN)
            remake();
    }

public:
    Skiplist(int maxn = 2e7 + 10, double P = 0.5)
    {
        p = P;
        maxkey = maxn;
        nowlevel = 0;
        srand(time(NULL));
        maxl = ceil(log(maxn) / log(1 / p)) - 1;
        head = new SkiplistNode<K, E>(maxn, -1, maxl);
        tail = new SkiplistNode<K, E>(maxn, -1, 0);
        for (int i = 0; i <= maxl; ++i)
            head->next[i] = tail;
        tail->next[0] = NULL;
        last = new SkiplistNode<K, E> *[maxl + 1];
        numsize = 0;
        nowidx = 0;
        bestN = 10;
        ops = 0;
    }

    bool search(int target)
    {
        if (target >= maxkey)
            return false;
        SkiplistNode<K, E> *befnode = head;
        for (int i = nowlevel; i >= 0; --i)
            while (befnode->next[i]->ele.first < target)
                befnode = befnode->next[i];
        if (befnode->next[0]->ele.first == target)
            return true;
        return false;
    }

    SkiplistNode<K, E> *searchlast(int tar)
    {
        if (tar >= maxkey)
            return NULL;
        SkiplistNode<K, E> *befnode = head;
        for (int i = nowlevel; i >= 0; --i)
        {
            while (befnode->next[i]->ele.first < tar)
                befnode = befnode->next[i];
            last[i] = befnode;
        }
        return befnode;
    }

    void add(int num)
    {
        int k = num, e = num;
        searchlast(k);
        ++nowidx;
        int l = getL();
        if (l > nowlevel)
        {
            l = ++nowlevel;
            last[l] = head;
        }
        SkiplistNode<K, E> *node = new SkiplistNode<K, E>(k, e, l);
        for (int i = 0; i <= l; ++i)
        {
            node->next[i] = last[i]->next[i];
            last[i]->next[i] = node;
        }
        numsize++;
        check();
    }
    void add(pair<K, E> P)
    {
        int k = P.first, e = P.second;
        searchlast(k);
        ++nowidx;
        int l = getL();
        if (l > nowlevel)
        {
            l = ++nowlevel;
            last[l] = head;
        }
        SkiplistNode<K, E> *node = new SkiplistNode<K, E>(k, e, l);
        for (int i = 0; i <= l; ++i)
        {
            node->next[i] = last[i]->next[i];
            last[i]->next[i] = node;
        }
        numsize++;
        check();
    }

    void showlist()
    {
        SkiplistNode<K, E> *newnode = head;
        printf("key        lev        next*\n");
        while (newnode->next[0] != NULL)
        {
            cout << setw(8) << newnode->ele.first << ' ' << setw(8) << newnode->lev << " ";
            for (int i = 0; i <= newnode->lev; ++i)
            {
                printf("%8d ", newnode->next[i]->ele.first);
            }
            cout << endl;
            newnode = newnode->next[0];
        }
        return;
    }

    bool erase(int num)
    {
        if (num > maxkey)
            return false;
        SkiplistNode<K, E> *befnode = searchlast(num);
        if (befnode->next[0]->ele.first != num)
            return false;
        befnode = befnode->next[0];
        for (int i = 0; i <= nowlevel && last[i]->next[i] == befnode; ++i)//有重复key时需要保证指向的是当前节点,直接比较地址
            last[i]->next[i] = befnode->next[i];
        delete befnode;

        while (nowlevel > 0 && head->next[nowlevel] == tail)
            nowlevel--;
        numsize--;
        ops++;
        check();
        return true;
    }
};

int main()
{
    string ops;
    getline(cin, ops);
    string nums;
    getline(cin, nums);
    stringstream opss(ops);
    stringstream numss(nums);

    string op;
    Skiplist<int, int> skt;
    int num;
    while (opss >> op)
    {
        if (op == "add")
        {
            numss >> num;
            printf("add %d\n", num);
            skt.add(num);
        }
        if (op == "search")
        {
            numss >> num;
            printf("search %d %d\n", num, skt.search(num));
        }
        if (op == "erase")
        {
            numss >> num;
            printf("erase %d\n", num);
            skt.erase(num);
        }
        skt.showlist();
    }
    return 0;
}
  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值