块状链表

前置知识:分块、链表

链表的优点:插入操作更加快速,但如果要返回特定位置的元素,时间复杂度为O(N)。

故引入分块思想,将链表中的元素变为大小为的数组,链表大小为,故返回特定位置元素的时间复杂度降为

基本操作:

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
int sqn=sqrt(n);
int tot=0,len=0;
struct node
{
char d[2*sqn+5];
int size,l,r;
node()
{
memset(d,0,sizeof(0));
size=0;
l=0;
r=0;
}
void pb(char c)
{
d[size++]=c;
}
}sqnlist[sqn+5];
void check(int id)//分裂操作
{
if(sqnlist[id].size>2*sqn)
{
tot++;
for(int i=sqn;i<sqnlist[id].size;i++)
{
sqnlist[tot].pb(sqnlist[id].d[i]);
}
sqnlist[id].size=sqn;
sqnlist[tot].l=id;
sqnlist[tot].r=sqnlist[id].r;
sqnlist[sqnlist[id].r].l=tot;
sqnlist[id].r=tot;
}
}
char ask(int pos)//查询操作
{
int st=1;
while(pos>sqnlist[st].size)
{
pos-=sqnlist[st].size;
st=sqnlist[st].r;
}
return sqnlist[st].d[pos-1];
}
void insert(int pos,char ch)
{
int st=1;
if(pos>len++)//检查pos位置并更新len
{
while(sqnlist[st].r!=0)
{
st=sqnlist[st].r;
}
sqnlist[st].pb(ch);
check(st);
return;
}
for(st=1;pos>sqnlist[st].size&&sqnlist[st].r!=0;st=sqnlist[st].r)
{
pos-=sqnlist[st].size;
}
for(int i=sqnlist[st].size;i>=pos;i--)
{
sqnlist[st].d[i]=sqnlist[st].d[i-1];
}
sqnlist[st].d[pos-1]=ch;
sqnlist[st].size++;
check(st);
return;
}

易知,三种操作的时间复杂度都为,比较重要的是check操作,分裂size过大的节点以保持时间复杂度

注意一点,在插入元素的过程中,总元素的个数也在变,但我们一般用N的最大值来确定sqn

参考:

https://oi-wiki.org/ds/block-list/

https://blog.csdn.net/m0_49959202/article/details/119033095