感谢大佬的文章:普通平衡树-跳表 - Tibrella 的隙间 - 洛谷博客 (luogu.com.cn) orz orz orz

代码仅仅是大佬的代码做略微修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#include <iostream>
#include <random>
#include <ctime>

#define ll long long

using namespace std;

//总结: skip list简单好写,空间复杂度为 O(p*MAX_LEVEL*n),p为创建新节点的时候的概率,再这段程序中是1/4,
//因为skip list完全支持线性读写,所以将它封装成一个泛型容器也不困难,尤其是迭代器也很好写,但是因为要支持--操作的可能需要改写成双向链表。

const int N=1e5+10,MAX_LEVEL=12;//最大层数

std::random_device seed;
std::minstd_rand rng(seed());//生成随机数

struct Node{
int level;//其实写一个const,但是懒得弄了。
ll key;

struct nodePointer{
int span;//跨幅,方便get_rank,get_val,类似于平衡树中的node_count
Node*pointer;
nodePointer():span(0),pointer(nullptr){}//让它新建的时候为空。
nodePointer(Node*x):span(0),pointer(x){}
Node*operator->(){return pointer;}
operator Node*(){return pointer;}
}*next;//一个节点在生成的时候已经决定了它的level,这个是不会在改变的

~Node(){delete [] next;}//析构时删除next数组

Node():key(0),level(0){}
};

Node* new_node()
{
return new Node;
}

void del_node(Node* x)
{
delete x;
}

int random_level()//产生随机高度,其中, (rng()&1)&&(rng()&1) 表示 概率为 1/4
{
int ret=1;
while(ret<MAX_LEVEL&&(rng()&1)&&(rng()&1))
++ret;
return ret;
}

Node* create_node(const int &level,const ll & key)//生成一个节点
{
Node*ret=new_node();
ret->key=key;
ret->level=level;
ret->next=new Node::nodePointer[level];
return ret;
}

Node*head=create_node(MAX_LEVEL,0);//head为头结点,减少分类讨论,显然他一定是最高的层数
int level=0,length=0;

void insert(const ll &key)
{
Node*cur=head;
Node::nodePointer update[MAX_LEVEL];
int lst_pos[MAX_LEVEL+1];//记录每一层上一个节点的位置!!也就是下标。这是在为维护span做服务
lst_pos[level]=0;//因为下面我们是从 level-1 下标开始的,为了方便编写,直接让 level 为0,这也是上面为什么是 MAX_LEVEL+1

for(int i=level-1;i>=0;--i)
{
lst_pos[i]=lst_pos[i+1];//往下爬,但是下标不动

while(cur->next[i]&&cur->next[i]->key<key)
lst_pos[i]+=cur->next[i].span, //下标加上跨幅
cur=cur->next[i];

update[i]=cur; //需要更新span和next的点
}

int newlevel=random_level();//新节点生成高度

if(newlevel>level)//如果大于现在的高度,那么需要更新
{
for(int i=level;i<newlevel;++i)
update[i]=head, //因为上面的层数只有一个新节点,所以都先设置为头结点
update[i]->next[i].span=length, // 跨幅为length
lst_pos[i]=0; //上一个为0,这都是在进行初始化

level=newlevel;
}

cur=create_node(newlevel,key);//创建新节点,不用担心会丢失应该插入的节点的位置,因为update数组有在记录

for(int i=0;i<newlevel;++i)
{
cur->next[i]=update[i]->next[i]; //这就是链表的插入语句,
cur->next[i].span=update[i]->next[i].span-(lst_pos[0]-lst_pos[i]);
//更新新节点跨幅,lst_pos[0]表示在第0层上一个节点的位置,lst_pos[i]表示在第i层上一个节点的位置,这样就很好理解了。

update[i]->next[i].pointer=cur;//也是链表的插入语句
update[i]->next[i].span=lst_pos[0]-lst_pos[i]+1;//新增一个节点,所以加1
}

for(int i=newlevel;i<level;++i)//处理当newlevel小于level的情况,把上层的进行加加
++update[i]->next[i].span;

++length;
}

void erase(const ll &key)
{
Node*cur=head;
Node::nodePointer update[MAX_LEVEL];

for(int i=level-1;i>=0;--i)
{
while(cur->next[i]&&cur->next[i]->key<key)
cur=cur->next[i];
update[i]=cur;
}

cur=cur->next[0];//现在cur指向应该删除的节点位置。

for(int i=0;i<level;++i)//更新跨幅和next指针
if(update[i]->next[i]==cur)
update[i]->next[i].span+=cur->next[i].span-1,
update[i]->next[i].pointer=cur->next[i].pointer;
else
--update[i]->next[i].span;

while(level>1&&!head->next[level-1])
//这个地方注意,如果节点被删除可能会导致层数减少,层数减少的标准为head->next[level-1]==nullptr,
//从上向下减少
--level;

del_node(cur);//删除节点。
--length;
}

//下面两个查找,没什么好说的
int get_rank(const ll & key)
{
Node*cur = head;
int res=0;

for(int i=level-1;i>=0;--i)
while(cur->next[i]&&cur->next[i]->key<key)
res+=cur->next[i].span,
cur=cur->next[i];

return res+1;
}

ll find_by_rank(int k)
{
Node* cur=head;
for(int i=level-1;i>=0;--i)
while(cur->next[i]&&k-cur->next[i].span>0)
k-=cur->next[i].span,
cur=cur->next[i];
return cur->next[0]->key;
}

Node* prev(const ll & key)
{
Node*cur=head;
for(int i=level-1;i>=0;--i)
while(cur->next[i]&&cur->next[i]->key<key)
cur=cur->next[i];
return cur;
}

Node* next(const ll & key)
{
Node*cur=head;
for(int i=level-1;i>=0;--i)
while(cur->next[i]&&cur->next[i]->key<=key)
cur=cur->next[i];
return cur->next[0];
}

int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);

#ifdef LOCAL
clock_t c1 = clock();
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif

//-------------------------------------------------

int n,op;
ll x;
cin>>n;

while(n--)
{
cin>>op>>x;
switch(op)
{
case 1:insert(x);break;
case 2:erase(x);break;
case 3:cout<< get_rank(x) <<'\n';break;
case 4:cout<< find_by_rank(x) <<'\n';break;
case 5:cout<< prev(x)->key <<'\n';break;
case 6:cout<< next(x)->key <<'\n';break;
}
}


//-------------------------------------------------

#ifdef LOCAL
cout << "\nTime Used: \n" << clock() - c1 << " ms" << endl;
#endif

//system("pause");
return 0;
}

点击并拖拽以移动