块状链表详解:原理、实现与应用
创作时间:
作者:
@小白创作中心
块状链表详解:原理、实现与应用
引用
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;
}
通过这个例子,我们可以看到块状链表在处理大规模字符串操作时的高效性。它结合了链表的插入优势和数组的随机访问优势,是一种非常实用的数据结构。
热门推荐
古代气候研究将过去的海洋酸化与当前的趋势联系起来
汉堡与埃尔沃斯堡:足球队伍连接两座城市的故事
听说在广东,陈皮是像嫁妆一样的存在
旺山:苏州的世外桃源
反对意见是什么?如何有效提出和应对
流夜郎闻酺不预_李白的诗原文赏析及翻译
新学期营养指南:家长必知的学生营养餐五大原则
会计主体:不只是企业法人
高压配电柜常见故障元器件及其处理方法
朱砂手串有毒吗?专家教你安全佩戴的秘诀
海康NAS设备闪红灯故障处理指南
下眼皮一直抽搐跳动怎么办?原因分析与缓解方法全攻略
从中医视角解读失眠:九种体质类型与睡眠的关系
电影与社交媒体:性别与性取向观念的演变与心理健康影响的比较
趣谈《西游》:菩提祖师为什么不让孙悟空说是他师傅
音箱线不够长时应如何解决?解决音箱线长度问题的方法有哪些?
音响线接触不良怎么办?这些解决方案请收好
北京法国文化中心耕耘二十载 | 推广法国文化,深化法中文化互动
重磅!5A景区评定新标准正式发布!
草莓品种介绍(46种)
肩关节脱位手法复位方法
周心芳:古方今用妙手回春 医道合真药济苍生
2024年世界人口将达到81.6亿:人口增长逐渐放缓
修复 Windows 更新错误 0x80244022(已解决!)
葡超吉维森特和本菲卡赛事前瞻3.28分享
想安全跑马拉松?科学训练及身心准备缺一不可!
全面了解汽车结构,汽车各部件的工作原理动图大集合
书单丨这15本经典好书,读懂中国文化的深层结构
Tomcat如何配置? Tomcat服务器的安装与配置指南
Tomcat详细讲解(巨详细!!!)