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 - 跳跃表详注,常数小的主要内容,如果未能解决你的问题,请参考以下文章
ElasticSearch实战-Skip List 跳表算法(文档定位跳跃算法)