一种伪严格跳表的实现
一种伪严格跳表的实现
初始化严格跳表
记录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;
}