概述
如果要维护一个序列,我们通常会使用数组和链表
但是如果要在序列中插入一些元素,以上两种数据结构的复杂度都是O(n)
于是就有了块状链表,它是数组和链表的结合:即一个链表的节点是一个数组
如果使链表的每个节点大小<sqrt(n),相邻两个节点大小和>sqrt(n),则链表长度也保持在 [sqrt(n), 2*sqrt(n)]
这样插入和删除的复杂度都将变成O(sqrt(n))
具体操作
存储
struct Node{
char s[maxs];
int size,next;//分别存节点中元素个数和下一个节点
}a[maxs];
int remain[maxs],pos;
void init(){
for(int i=0;i<maxs;i++)remain[i]=i;
pos=1;
}
int newnode(){//返回一个未使用的节点
return remain[pos++];
}
void delnode(int x){//将x加到未使用的节点中
remain[--pos]=x;
}
定位
void Find(int &part,int &size){//查找第size个字符的节点,并存在part(哪个节点),size(节点中的第几个字符)中
for(part=1;part&&size>a[part].size;part=a[part].next){
size-=a[part].size;
}
}
分裂
void transfer(int old,int ne,int x){//将old的第x个字符之后分给ne
a[ne].next=a[old].next,a[ne].size=a[old].size-x;
a[old].next=ne,a[old].size=x;
memcpy(a[ne].s,a[old].s+x,a[ne].size);
}
void split(int part,int x){//将part节点从x处一分为二(x分到前面的节点中)
if(!part||x==a[part].size)return;
int t=newnode();
transfer(part,t,x);
}
合并
void merge(int x,int y){//合并x,y
memcpy(a[x].s+a[x].size,a[y].s,a[y].size);
a[x].next=a[y].next,a[x].size+=a[y].size;
delnode(y);
}
插入
void maintain(int x){//维护
for(;x;x=a[x].next){
for(int y=a[x].next;y&&a[x].size+a[y].size<=maxs;y=a[x].next){
merge(x,y);
}
}
}
void insert(int l,char *s,int size){//在l之后插入size个字符
int mark,pre=0,next,i;
Find(pre,l);split(pre,l);
mark=pre;
next=a[pre].next;
for(i=0;i+maxs<=size;i+=maxs){
a[pre].next=newnode();
pre=a[pre].next;
a[pre].size=maxs;
memcpy(a[pre].s,s+i,maxs);
}
if(size-i){
a[pre].next=newnode();
pre=a[pre].next;
a[pre].size=size-i;
memcpy(a[pre].s,s+i,a[pre].size);
}
a[pre].next=next;
maintain(mark);
}
删除
void earse(int l,int size){//删除l后的size个字符
int pre=0,next;
Find(pre,l);split(pre,l);
for(next=a[pre].next;next&&size>a[next].size;next=a[pre].next){
size-=a[next].size;a[pre].next=a[next].next;delnode(next);
}
split(next,size);
a[pre].next=a[next].next;delnode(next);
maintain(pre);
}
查找
void copy(int l,int size,char *s){//将l后的size个字符存在s中
int pre=0,next,k,i=0;
Find(pre,l);
i=k=min(a[pre].size-l,size);
memcpy(s,a[pre].s+l,k);
for(next=a[pre].next;next&&i<size;next=a[next].next){
k=min(a[next].size,size-i);
memcpy(s+i,a[next].s,k);i+=k;
}
}
例题
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxs (1<<11) struct Node{ char s[maxs]; int size,next; }a[maxs]; int remain[maxs],pos; void init(){ for(int i=0;i<maxs;i++)remain[i]=i; pos=1; } int newnode(){ return remain[pos++]; } void delnode(int x){ remain[--pos]=x; } void Find(int &part,int &size){ for(part=1;part&&size>a[part].size;part=a[part].next){ size-=a[part].size; } } void transfer(int old,int ne,int x){ a[ne].next=a[old].next,a[ne].size=a[old].size-x; a[old].next=ne,a[old].size=x; memcpy(a[ne].s,a[old].s+x,a[ne].size); } void split(int part,int x){ if(!part||x==a[part].size)return; int t=newnode(); transfer(part,t,x); } void merge(int x,int y){ memcpy(a[x].s+a[x].size,a[y].s,a[y].size); a[x].next=a[y].next,a[x].size+=a[y].size; delnode(y); } void maintain(int x){ for(;x;x=a[x].next){ for(int y=a[x].next;y&&a[x].size+a[y].size<=maxs;y=a[x].next){ merge(x,y); } } } void insert(int l,char *s,int size){ int mark,pre=0,next,i; Find(pre,l);split(pre,l); mark=pre; next=a[pre].next; for(i=0;i+maxs<=size;i+=maxs){ a[pre].next=newnode(); pre=a[pre].next; a[pre].size=maxs; memcpy(a[pre].s,s+i,maxs); } if(size-i){ a[pre].next=newnode(); pre=a[pre].next; a[pre].size=size-i; memcpy(a[pre].s,s+i,a[pre].size); } a[pre].next=next; maintain(mark); } void earse(int l,int size){ int pre=0,next; Find(pre,l);split(pre,l); for(next=a[pre].next;next&&size>a[next].size;next=a[pre].next){ size-=a[next].size;a[pre].next=a[next].next;delnode(next); } split(next,size); a[pre].next=a[next].next;delnode(next); maintain(pre); } void copy(int l,int size,char *s){ int pre=0,next,k,i=0; Find(pre,l); i=k=min(a[pre].size-l,size); memcpy(s,a[pre].s+l,k); for(next=a[pre].next;next&&i<size;next=a[next].next){ k=min(a[next].size,size-i); memcpy(s+i,a[next].s,k);i+=k; } } char s[1<<21|1]; int main(){ init(); int n,x=0,l;scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%s",s); if(s[0]=='M')scanf("%d",&x); else if(s[0]=='P')x--; else if(s[0]=='N')x++; else if(s[0]=='I'){ scanf("%d",&l); for(int i=0;i<l;i++){ s[i]=getchar(); while(s[i]=='\n'||s[i]=='\r')s[i]=getchar(); } insert(x,s,l); } else if(s[0]=='D'){ scanf("%d",&l); earse(x,l); } else if(s[0]=='G'){ scanf("%d",&l); copy(x,l,s);s[l]='\0'; printf("%s\n",s); } } return 0; }