skip list - 跳跃表详注,常数小

Posted dgklr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了skip list - 跳跃表详注,常数小相关的知识,希望对你有一定的参考价值。

跳跃表详注

具体看注释代码

luoguP3369: https://www.luogu.org/recordnew/show/11782419

技术分享图片
 1 #include<bits/stdc++.h>
 2 #define repeat(a,b,c,d) for (int a=b;a<=c;a+=d)
 3 using namespace std;
 4 struct node{
 5     int nxt,dwn,jmp,val;
 6 }a[100000 * 4];
 7 int al = 0,n,first;
 8 const int maxdep = 9, INF = 1e9;
 9 inline void build(){//在程序开始时调用,建造一个dep=maxdep的表
10     for (register int i=1;i<=maxdep;i++){//建造开始节点
11         a[++al].nxt = maxdep + i;//指向最后节点
12         if (i != 1) a[al].dwn = al-1;//非第一个节点指向下一个down节点
13         else a[al].dwn = -1;//最后一个
14         a[al].jmp = 0;a[al].val = -INF;//赋值,a[al].jmp可以任意赋。
15     }
16     first = al;//指向第一个节点
17     for (register int i=1;i<=maxdep;i++){
18         a[++al].nxt = -1;//same
19         if (i != -1) a[al].dwn = al-1;
20         a[al].jmp = INF;a[al].val = INF;
21     }
22 }
23 //之后我们维护NOW指针。
24 inline int dfs_insert(int x,int NOW,int dep,int insdep){//为了常数优化,不先调用find_k_rank_and_p找位置,而是直接递归位置。
25     if (dep == 0) return 0;//边界
26     register int len_jmp = 0;//我们从现在NOW指针往前蹦的长度
27     while (x > a[a[NOW].nxt].val) len_jmp += a[NOW].jmp, NOW = a[NOW].nxt;//**记住,所有的skip list的搜索都是开区间的!**
28     register int lft_jmp = dfs_insert(x,a[NOW].dwn,dep-1,insdep);//搜到下一层
29     if (dep > insdep) a[NOW].jmp++;//如果没达到指定层数,只是NOW的jmp指针增加
30     else{
31         a[++al].nxt = a[NOW].nxt;a[NOW].nxt = al;a[al].dwn = al-1;a[al].val = x;//维护指针
32         a[al].jmp = a[NOW].jmp - lft_jmp;a[NOW].jmp = lft_jmp + 1;//一并维护NOW
33     }
34     return len_jmp + lft_jmp;//返回跳过的层数。
35 }
36 inline void insert(int x){
37     register int insdep = 1,random = rand()<<16|rand();//**千万不要rand来rand去,贼慢!
38     while (random%4 == 0) insdep++,random/=4;dfs_insert(x,first,maxdep,insdep);//亲测4层最快
39 }
40 inline pair<int,int> find_k_rank_and_p(int x){
41     register int NOW = first,len = 0,dep = maxdep;//寻找x的rank并且返回NOW指针(全部开区间)
42     while (dep > 0){
43         while (x > a[a[NOW].nxt].val) len += a[NOW].jmp, NOW = a[NOW].nxt;//能走则走
44         dep--;//维护层数
45         if (dep == 0) break;//为了维护NOW,一旦dep==0就break;
46         NOW = a[NOW].dwn;
47     }
48     return make_pair(len+1,NOW);//len是开区间,要+1
49 }
50 inline void del(int x){
51     register int rank = find_k_rank_and_p(x).first,NOW = first,len = 0,dep = maxdep;//删除时先找出要删的位置,必须保证不多删。
52     while (dep > 0){
53         while (x > a[a[NOW].nxt].val) len += a[NOW].jmp, NOW = a[NOW].nxt;//
54         if (a[a[NOW].nxt].val == x && len + a[NOW].jmp == rank) // be del
55             a[NOW].jmp = a[NOW].jmp + a[a[NOW].nxt].jmp - 1,a[NOW].nxt = a[a[NOW].nxt].nxt;//维护NOW指针
56         else a[NOW].jmp--;
57         NOW = a[NOW].dwn;
58         dep--;
59     }
60 }
61 inline int find_rank_val(int x){
62     register int NOW = first,len = 0,dep = maxdep;
63     while (dep > 0){
64         while (x > len + a[NOW].jmp)
65             len += a[NOW].jmp, NOW = a[NOW].nxt;//能蹦则蹦
66         dep--;
67         if (dep == 0) break;
68         NOW = a[NOW].dwn;
69     }
70     return a[a[NOW].nxt].val;//返回值
71 }
72 int main(){
73     srand(time(NULL));
74     build();
75     scanf("%d",&n);
76     for (int i=1;i<=n;i++){
77         register int opt,x;
78         scanf("%d%d",&opt,&x);
79         if (opt == 1) insert(x);//插入
80         if (opt == 2) del(x);//删除
81         if (opt == 3) printf("%d
",find_k_rank_and_p(x).first);//查找x数位置
82         if (opt == 4) printf("%d
",find_rank_val(x));//查找rank为x的值
83         if (opt == 5) printf("%d
",a[find_k_rank_and_p(x).second].val);//求x的前驱
84         if (opt == 6) printf("%d
",a[a[find_k_rank_and_p(x+1).second].nxt].val);//求x的后缀
85     }
86 }
Skip List

 

以上是关于skip list - 跳跃表详注,常数小的主要内容,如果未能解决你的问题,请参考以下文章

探索Skip List (跳跃表)

高级数据结构 ---- 跳跃表(Skip List)

跳跃表(skip list)

ElasticSearch实战-Skip List 跳表算法(文档定位跳跃算法)

ElasticSearch实战-Skip List 跳表算法(文档定位跳跃算法)

Redis必知必会之zset底层—Skip List跳跃列表(面试加分项)