1、概念
双链表是一种 线性数据结构,每个节点包含 数据域 和 两个指针域,分别指向前驱和后继节点。相比单链表,双链表支持 双向遍历,但占用更多内存。
基本的操作:插入数据、删除数据;
注意点:左右插入数据、索引问题
模板:
struct Node {
int val; // 数据域
Node* prev; // 前驱指针
Node* next; // 后继指针
};
2、模板
通过数组维护一个双链表,能实现数据的插入和删除!
const int N = 1e5 + 10; // 预分配足够大的空间
int idx; // 当前可用的节点索引
int val[N], l[N], r[N]; // val存储值,l[]存储左指针,r[]存储右指针
// 初始化双链表(使用哨兵节点简化操作)
void init() {
r[0] = 1; // 头节点0的next指向尾节点1
l[1] = 0; // 尾节点1的prev指向头节点0
idx = 2; // 0和1已被占用,第一个数据节点从索引2开始
// 因此实际第k个插入的节点对应索引 k+1
}
// 在节点k的右侧插入新节点x
void add(int k, int x) {
val[idx] = x; // 新节点赋值
r[idx] = r[k]; // 新节点的next指向k的原next节点
l[idx] = k; // 新节点的prev指向k
l[r[k]] = idx; // k的原next节点的prev指向新节点
r[k] = idx; // k的next指向新节点(必须最后执行!)
// 如果先执行这一步,l[r[k]]会指向错误位置
idx++; // 分配新索引
}
// 删除节点k
void re(int k) {
l[r[k]] = l[k]; // k的next节点的prev指向k的prev
r[l[k]] = r[k]; // k的prev节点的next指向k的next
}
注意点:
1、左右哨兵的设计;在最左最右插入数据的逻辑;
2、在插入更新时,注意r[k]要最后更新;
小贴士:
在全局域(main函数以外)定义变量并初始化是可以的,但是执行语句是非法的!!!!
例如:
const int N = 1e5 + 10; // ✅ 合法:全局变量的声明 + 初始化
int l[N], r[N]; // ✅
l[1] = 0; // ❌ 非法:全局作用域不能直接执行赋值语句
r[0] = 1; // ❌ 非法
int main(){ }