此篇接续上一篇第六题中的手搓双链表的内容,上一篇博客链接:第十四届蓝桥杯省赛C++B组题目及解析(二)-CSDN博客
这里通过acwing中的模板题进行讲解
首先可以看一下模拟单链表的实现方法
一.数组模拟单链表
一般用于邻接表(n个链表),用于存储图和树
数据结构由值和下一个结点的指针两部分构成
1.值value:用e[N]表示某个点的值
2.指针next:用ne[N]表示某个点的next指针的值
3.头指针head:用head表示指向链表第一个元素的头指针
int head, idx;
int e[N], ne[N];
在实际使用过程中还需要加入一个idx,用来计算当前存入的元素的个数,idx用于给新加入的元素分配新的位置。
链表的函数:
1.初始化
void init()
{
head = -1;
idx = 0;
}
表示头结点指向空(-1),当前元素个数是0个,还没有放入元素。
2.插入(头插,将结点插入到head指针和第一个元素中间)
void add_to_head(int x)
{
e[idx] = x;
ne[idx] = head;
head = idx;
idx ++;
}
先将值存入e数组中,然后将该结点的指针指向head指向的节点,将head指向这个新的结点的位置,该位置被新元素使用过了因此最后++。
3.删除(删除位置为k后的节点)
void remove(int k)
{
ne[k] = ne[ne[k]];
}
指针跳过k后面的那个节点,指向k的后继的后继的那个节点
看个题目:
在该题目中需要加入一个在结点k之后插入一个新的值为x的结点的操作,代码如下:
#include
using namespace std;
typedef long long LL;
int n;
const int N = 100010;
int head, idx;
int e[N], ne[N];
void init() {
head = -1;
idx = 1;
}
void add_to_head(int x) {
e[idx] = x;
ne[idx] = head;
head = idx;
idx ++;
}
void remove(int k) {
ne[k] = ne[ne[k]];
}
void add1(int k, int x) {
e[idx] = x;
ne[idx] = ne[k];
ne[k] = idx;
idx ++;
}
int main() {
ios::sync_with_stdio(false);
init();
cin >> n;
while (n --) {
char c;
cin >> c;
if (c == 'H') {
int x;
cin >> x;
add_to_head(x);
}
if (c == 'D') {
int k;
cin >> k;
if(!k)
{
head = ne[head];
continue;
}
remove(k);
}
if (c == 'I') {
int k, x;
cin >> k >> x;
add1(k, x);
}
}
for(int i = head; i != -1; i = ne[i])
cout << e[i] << " ";
return 0;
}
由于题目说明中存在k=0的删除情况,因此需要特判一下k是否等于0,如果等于0则需要直接将head跳过第一个元素指向第二个元素。运行后通过所有测试用例
二.数组模拟双链表
一般用于优化问题
数据结构由值,前驱指针和后继指针三部分构成
1.值value
2.前驱指针l[N]
3.后继指针r[N]
函数:
1.初始化
void init()
{
r[0] = 1;
l[1] = 0;
idx = 2;
}
这里初始化采用一种比较简洁的写法,用0表示双链表的第一个元素的下标(head),用1表示双链表的最后一个元素的下标(tail),由于0和1已经被占用,因此idx下标需要从2开始
2.插入(下标为k的右边插入值为x的点)
void add(int k, int x)
{
e[idx] = x;
r[idx] = r[k];
l[idx] = k;
l[r[k]] = idx;
r[k] = idx;
idx ++;
}
画图更方便理解,由于涉及到很多的指针操作图片涂涂改改不容易看,就不放图片演示
3.删除(删除下标为k的节点)
void remove(int k)
{
r[l[k]] = r[k];
l[r[k]] = l[k];
}
让左边的右边等于右边,右边的左边等于左边就可以了(不是
题目如下:
敲一下主函数,完整代码如下:
#include
using namespace std;
typedef long long LL;
const int N = 100010;
int m;
int e[N], l[N], r[N];
int idx;
void init() {
r[0] = 1;
l[1] = 0;
idx = 2;
}
void add(int k, int x) {
e[idx] = x;
r[idx] = r[k];
l[idx] = 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() {
ios::sync_with_stdio(false);
cin >> m;
init();
while (m --) {
string c;
int x, k;
cin >> c;
if (c == "L") {
cin >> x;
add(0, x);
}
if (c == "R") {
cin >> x;
add(l[1], x);
}
if (c == "D") {
cin >> k;
remove(k + 1);
}
if (c == "IL") {
cin >> k >> x;
add(l[k + 1], x);
}
if (c == "IR") {
cin >> k >> x;
add(k + 1, x);
}
}
for (int i = r[0]; i != 1; i = r[i])
cout << e[i] << " ";
return 0;
}
由于0和1结点下标用于双链表的头和尾,因此对于输入的k需要加一后才是函数进行操作的元素的下标。通过所有的测试用例