块状链表全纪录

块状链表的简单介绍

块状链表,可以说是一种很犯规的数据结构
支持动态的序列加入删除,询问区间和之类的操作

同时拥有数组和链表的优点:
数组: 所有数据在内存中是紧凑储存的,优点是定位快:O(1)
链表: 通过指针将不同位置的元素链接起来:修改快 O(1)

总的来说重要操作只有几个:
定位,分裂,插入,合并


块状链表基础操作

虽然说是链表,但是在实现的时候,我们还是用数组模拟
块状链表的空间比较奇怪,每个块的大小在 sqrt(n)22sqrt(n) s q r t ( n ) 2 − 2 s q r t ( n ) ,大多数情况下会偏大

初始化
数组的空间并不优秀,所以我们要时刻节俭,队列q中记录的就是可用的结点编号
因此我们申请结点和删除结点都需要在队列q的基础上进行

void init()
{
    for (int i=1;i<=blocknum;i++) q.push(i);
    a[0].len=0; a[0].nxt=-1;
}
int newnode()
{
    int t=q.front(); q.pop();
    return t;
}
void delnode(int t)
{
    q.push(t);
}

在位置p插入一段数据,第一步就是定位
(默认链表起点就是0,邻接表遍历,每一个结点中下标从0开始

void find(int &pos,int &now)     //从第一个块开始搜索,搜索位置pos所属的块的编号  
{
    for (now=0;a[now].nxt!=-1&&pos>a[now].len;now=a[now].nxt) pos-=a[now].len;
}

定位完成后,我们需要将该块分裂
不是特别理解memcpy的用法?
简单来说,如果我们需要把 a a 数组从i位置开始的长度 len l e n 复制到 b b 数组j位置,则

memcpy(b+j,a+i,len) m e m c p y ( b + j , a + i , l e n )

(亲测了一下,发现如果是数组类型只能全部复制,只有字符串可以部分copy,难道是我的姿势不对?)

void fillnode(int pos,int n,char data[],int nxt)
{
    a[pos].nxt=nxt; a[pos].len=n;
    memcpy(a[pos].data,data,n);  //将data中前n个元素复制到块pos中
}
void divide(int pos,int p)
{
    if (a[pos].len==p) return;
    int t=newnode();
    fillnode(t,a[pos].len-p,a[pos].data+p,a[pos].nxt);
    a[pos].nxt=t; 
    a[pos].len=p;
}

每次插入删除之后,我们都要合并一下小结点

void maintain(int pos)   //当前块与后一个块合并 
{
    int t;
    for (;pos!=-1;pos=a[pos].nxt)
        for (t=a[pos].nxt;t!=-1&&a[pos].len+a[t].len<blocksize;t=a[t].nxt)
        {
            memcpy(a[pos].data+a[pos].len,a[t].data,a[t].len);
            a[pos].len+=a[t].len;
            a[pos].nxt=a[t].nxt;
            delnode(t);
        }
}

大体上来说就是这些东西
代码中有些小细节,只要理解到位就没什么问题了
记住块里的下标从0开始,牵扯到find和divide中的pos一定要大于0

const int N=1<<25;
const int blocksize=20000;
const int blocknum=N/blocksize*3;
int n;
char s[N+10];
struct node{
    char data[blocksize+10];
    int len,nxt;
};
node a[blocknum+10];
queue<int> q;

int newnode() {
    int t=q.front(); q.pop();
    return t;
}

void delnode(int t) {
    q.push(t);
}

void find(int &pos,int &now) {
    for (now=0;now!=-1&&a[now].len<pos;now=a[now].nxt) pos-=a[now].len;
    //保证pos>0: a[now].len<pos
}

void fillnode(int pos,int n,char *data,int nxt) {
    int t=newnode();
    memcpy(a[t].data,data,n);   //memcpy就说明有结点发生变化,需要维护len和nxt 
    a[t].nxt=nxt;
    a[t].len=n;
}

void divide(int now,int pos) {
    if (pos==a[now].len) return;
    int t=newnode();
    fillnode(t,a[data].len-pos,a[now].data+pos,a[now].nxt);
    a[now].nxt=t;
    a[now].len=pos;
}

void maintain(int now) {
    for (;now!=-1;now=a[now].nxt)
        for (int t=a[now].nxt;t!=-1&&a[now].len+a[t].len<blocksize;t=a[t].nxt) {
            memcpy(a[now].data+a[now].len,a[t].data,a[t].len);
            a[now].len+=a[t].len;
            a[now].nxt=a[t].nxt;
            delnode(t);    //删除结点 
        }
}

void insert(int pos,int n) {
    int now,i=0,t;
    find(pos,now);
    divede(now,pos);
    for (i=0;i+blocksize<=n;i+=blocksize) {     //i+blocksize<=n
        int t=newnode();
        fillnode(t,blocksize,s+i,a[now].nxt);
        a[now].nxt=t;
        now=t;
    }
    if (i<n) {
        int t=newnode();
        fillnode(t,n-i,s+i,a[now].nxt);
        a[now].nxt=t;
    }
    maintain(now);
}

void del(int pos,int n) {
    int now,i,t;
    find(pos,now);
    divide(now,pos);
    for (i=a[now].nxt;i!=-1&&a[i].len<n;i=a[i].nxt)
        n-=a[i].len;
    divide(i,n);       //保证n>0: a[i].len<n
    i=a[i].nxt;
    for (t=a[now].nxt;t!=i;t=a[now].nxt) {  //注意t的循环: t=a[now].nxt 
        a[now].nxt=a[t].nxt;
        delnode(t);
    }
    maintain(now);
}

void get(int pos,int n) {
    int now,i,t;
    find(now,pos);
    i=min(n,a[now].len-pos);
    memcpy(s,a[now].data+pos,i);
    for (t=a[now].nxt;t!=-1&&i+a[t].len<=n;t=a[t].nxt) {
        memcpy(s+i,a[t].data,a[t].len);
        i+=a[t].len;
    }
    if (i<n) memcpy(s+i,a[t].data,n-i);
    s[n]=0;
}

void init() {
    for (int i=1;i<=blocknum;i++) q.push(i);
    a[0].len=0; a[0].nxt=-1;
}

只写过一道个其他算法结合的题目:块状链表+并查集

块状链表终极考验!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值