块状链表详解:原理、实现与应用
创作时间:
作者:
@小白创作中心
块状链表详解:原理、实现与应用
引用
1
来源
1.
https://oi-wiki.org/ds/block-list/
块状链表(Block List)是一种高效的数据结构,结合了链表和数组的优点,特别适合处理大规模数据的插入、删除和查询操作。本文将详细介绍块状链表的基本概念、实现方法以及在算法竞赛中的应用。
块状链表的基本思想是将一个长数组分割成多个块,每个块用一个链表节点表示,节点内部是一个固定大小的数组。这种结构可以有效平衡随机访问和插入/删除操作的效率。
块状链表的实现
块状链表应该至少支持:分裂、插入、查找。 什么是分裂?分裂就是分裂一个
node
,变成两个小的
node
,以保证每个
node
的大小都接近(否则可能退化成普通数组)。当一个
node
的大小超过时执行分裂操作。
分裂操作怎么做呢?先新建一个节点,再把被分裂的节点的后个值
copy
到新节点,然后把被分裂的节点的后个值删掉(
size--
),最后把新节点插入到被分裂节点的后面即可。
块状链表的所有操作的复杂度都是的。
还有一个要说的。 随着元素的插入(或删除),会变,也会变。这样块的大小就会变化,我们难道还要每次维护块的大小?
其实不然,把设置为一个定值即可。比如题目给的范围是,那么就设置为大小为的常量,不用更改它。
1 list<vector
结构体定义
struct node {
node* nxt;
int size;
char d[(sqn << 1) + 5];
node() {
size = 0, nxt = NULL;
memset(d, 0, sizeof(d));
}
void pb(char c) {
d[size++] = c;
}
};
基本操作
- 分裂操作:当一个节点的大小超过预设阈值时,需要将其分裂成两个节点,以保持每个节点的大小接近。
- 插入操作:在指定位置插入元素,可能需要更新多个节点。
- 查找操作:通过遍历链表和数组索引实现元素查找。
STL中的rope容器
STL中的rope容器也起到块状链表的作用,它采用可持久化平衡树实现,可完成随机访问和插入、删除元素的操作。虽然rope并不是真正的用块状链表来实现,但其功能与块状链表类似,时间复杂度相当于可持久化平衡树的复杂度(即O(log n))。
基本操作
操作 | 作用 |
---|---|
rope<int> a | 初始化rope(与vector等容器很相似) |
a.push_back(x) | 在a的末尾添加元素x |
a.insert(pos, x) | 在a的pos个位置添加元素x |
a.erase(pos, x) | 在a的pos个位置删除x个元素 |
a.at(x)或a[x] | 访问a的第x个元素 |
a.length()或a.size() | 获取a的大小 |
例题:POJ2887 Big String
这是一道经典的块状链表应用题目,主要考察块状链表的插入和查询操作。以下是完整的代码实现:
#include <cctype>
#include <cstring>
#include <iostream>
using namespace std;
const int sqn = 1e3;
struct node {
node* nxt;
int size;
char d[(sqn << 1) + 5];
node() {
size = 0, nxt = NULL;
}
void pb(char c) {
d[size++] = c;
}
};
char inits[(int)1e6 + 5];
int llen, q;
void readch(char& ch) {
do cin >> ch;
while (!isalpha(ch));
}
void check(node* p) {
if (p->size >= (sqn << 1)) {
node* q = new node;
for (int i = sqn; i < p->size; i++)
q->pb(p->d[i]);
p->size = sqn, q->nxt = p->nxt, p->nxt = q;
}
}
void insert(char c, int pos) {
node* p = head;
int tot, cnt;
if (pos > llen++) {
while (p->nxt != NULL)
p = p->nxt;
p->pb(c), check(p);
return;
}
for (tot = head->size; p != NULL && tot < pos; p = p->nxt, tot += p->size);
tot -= p->size, cnt = pos - tot - 1;
for (int i = p->size - 1; i >= cnt; i--)
p->d[i + 1] = p->d[i];
p->d[cnt] = c, p->size++;
check(p);
}
char query(int pos) {
node* p;
int tot;
for (p = head, tot = head->size; p != NULL && tot < pos; p = p->nxt, tot += p->size);
tot -= p->size;
return p->d[pos - tot - 1];
}
int main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> inits >> q;
llen = strlen(inits);
node* p = new node;
head = p;
for (int i = 0; i < llen; i++) {
if (i % sqn == 0 && i)
p->nxt = new node, p = p->nxt;
p->pb(inits[i]);
}
char a;
int k;
while (q--) {
readch(a);
if (a == 'Q')
cin >> k, cout << query(k) << '\n';
else
readch(a), cin >> k, insert(a, k);
}
return 0;
}
通过这个例子,我们可以看到块状链表在处理大规模字符串操作时的高效性。它结合了链表的插入优势和数组的随机访问优势,是一种非常实用的数据结构。
热门推荐
桃胶:营养益处与食用方法全解析——是否真的堪比燕窝?
狧糠及米相关的成语是什么
一窍不通怎么学基金:从零开始的基金学习指南
故宫文创产品:传统文化与现代创意的完美结合
潮湿天墙身发霉?7个霉菌清洁方法:梳打粉/白醋可处理!
无人机助力打击毒品犯罪:神秘原理助力罂粟花识别
普洱市:茶马古道文化遗产保护与文旅融合的创新实践
原木风软装避坑指南:从配色到家具的教科书级搭配法则
法律咨询服务收费标准及其法律适用性分析
Qt数据库开发入门教程:从连接配置到性能优化
数据分析平台的六大类数据来源及应用指南
黑蚂蚁泡酒的正确方法与制作技巧:最佳酒品选择与泡酒比例
黄子韬直播送30台纯电汽车:品牌方赞助15台,每辆售价超12万
二审上诉与再审:司法程序中的重要区别
湖南一地出现大量“鸡公虫”,半天内啃光一棵树,它有哪些危害?
动物内脏是“营养小宝库”,注意4点放心吃
期货套利机会的发现方法及其局限性
房屋裂缝鉴定标准:如何判断分析房屋裂缝、变形和倾斜的种类和原因?
保护胰腺:长轻营养食疗为您找到这5个方法,尽早用!
如何选择适合的高速车型?这些车型有哪些特点和优势?
阿根廷即将提高入境行李商品免税额度,最高可达3000美元
什么是历史解释
层次分析法AHP:一种综合评价及决策方法
全国第一条土豆米生产线在昭通巧家正式投产运行!马铃薯里的新质生产力
大宋南衙开封府 创新发展谱新篇
武汉会战:抗日战争中最大规模的战役,他是如何扭转战局的?
1MW等于多少KW?电力计量单位换算详解
居间合同纠纷的证据收集与保全
汽车电瓶搭电,到底是“先正极”还是“先负极”?搞错顺序要吃亏
30多年工龄副科级公务员晒工资:5000多元,网友:不可能吧?