双链表基础——定义&模板&例题&插入&删除
双链表是单链表的加强版——它可以从前往后遍历,也可以从后往前遍历,而且在遍历的过程中,当前结点的指针可以选择指向前面的结点也可以选择指向后面的结点,这使得它十分地灵活;
我们今天来看一道acwing的题目;
双链表定义&模板
双链表就是单链表加上一个前驱指针域;
还记得单链表的数据结构是说明样子的吗:
而这是双链表:
单链表 = 数据域 + 后驱指针域;
struct nodes{
nodes* next;
int data;
};
双链表 = 前驱指针域 + 数据域 + 后驱指针域;
struct nodes{
nodes* pre;
nodes* next;
int data;
};
单链表设置了一个头结点,方便我们从头结点开始遍历数据,设置了一个入口;
而双链表设置了一个头结点和一个尾结点,我们可以从头开始遍历数据,或者从尾部开始遍历数据;
这里提供一个y总的双链表模板,来自acwing网站;
// e[]表示节点的值,l[]表示节点的左指针,r[]表示节点的右指针,idx表示当前用到了哪个节点
int e[N], l[N], r[N], idx;
// 初始化
void init()
{
//0是左端点,1是右端点
r[0] = 1, l[1] = 0;
idx = 2;
}
// 在节点a的右边插入一个数x
void insert(int a, int x)
{
e[idx] = x;
l[idx] = a, r[idx] = r[a];
l[r[a]] = idx, r[a] = idx ++ ;
}
// 删除节点a
void remove(int a)
{
l[r[a]] = l[a];
r[l[a]] = r[a];
}
作者:yxc
链接:https://www.acwing.com/blog/content/404/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
双链表例题
题解如下:
#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int m;
int e[N],l[N],r[N];
int idx;//idx是代表当前这个位置;
//双链表初始化
void inti(){
l[1] = 0 , r[0] = 1;//将头结点和尾结点连起来
idx = 2 ; //2是新的点,因为我们将依次开辟空间放入更多的结点,我们既然已经用过0和1了,自然就轮到2了
}
// 在第k个点的右边插入一个x
void add(int k, int x){
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
l[r[k]] = idx ;
r[k] = idx;
idx ++ ;
}
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
}
int main(){
int t;
ios::sync_with_stdio(false);
cin>>t;
inti();
while(t--){
string op;
cin>>op;
int k , x ;
if(op == "R" ){
cin>>x;
add(l[1],x);
}
else if(op == "L"){
cin >> x;
add(0 , x);
}
else if(op == "D"){
cin >> k ;
remove( k + 1 );
}
else if(op == "IL"){
cin>> k >> x;
add(l[k + 1] , x);
}
else
{
cin >> k >> x;
add( k + 1 , x );
}
}
for(int i = r[0] ; i != 1 ; i = r[i] )cout<<e[i]<<" ";
return 0;
}
插入操作
插入操作非常讲究顺序:
先将当前的结点idx 向左 连接至结点k;
其次将当前的结点idx 向右 连接至结点k右边的结点r[k];
然后是将结点k右边的结点r[k] 向左 连接至当前结点idx;
最后将结点k 向右 连接至当前结点idx;
// 在第k个点的右边插入一个x
void add(int k, int x){
e[idx] = x;
l[idx] = k;
r[idx] = r[k];
l[r[k]] = idx ;
r[k] = idx;
idx ++ ;
}
删除操作
void remove(int k){
r[l[k]] = r[k];
l[r[k]] = l[k];
}