这里依然是用数组来模拟双链表。双链表中有一个头指针 head 和一个尾指针 tail,为了模拟方便,我们将 head 固定在模拟数组的下标 0 的位置,tail 固定在模拟数组下标 1 的位置。即 idx 初始化后从 2 开始。
void init()
{
// r[1] l[0] 恒为空
r[0] = 1; // head 向后指向 tail
l[1] = 0; // tail 向前指向 head
idx = 2;
}
初始化后就可以对链表进行插入结点了,有四种插入方法:
首先要说的是这四个插入位置用的是同一个函数!
// 都可以看作是在第 k 个结点右边插入一个结点
void add(int k, int x)
{
e[idx] = x; // 存储数据
l[idx] = k; // ① 当前结点的左指针指向第 k 个结点
r[idx] = r[k]; // ② 当前结点的右指针设为第 k 个结点的右指针指向
l[r[k]] = idx; // ③ 第 k 个结点右指针指向结点的左指针设置为当前结点
r[k] = idx; // ④ 第 k 个结点的右指针指向当前节点
idx ++;
}
不同的在于传入的实参上。我们分别看起:
说明一下:以下的操作顺序皆为个人习惯问题
·
1.在链表最左端插入结点:
当输入为一个 L
时,我们可以理解为在第 0 个插入的结点的右边插入一个结点。即 L ---> add(0, x)
。
2.在第 k 个插入的数的右边插入结点
如果我们要在第 k 个插入的结点的右边插入一个结点的话,由单链表总结出来的结论,我们需要修改第 k 个插入结点的指针情况。在上例中我们要在第 1 个结点右边插入的话,要修改数组下标为 2 的指针指向,因此要执行 add(2,x2)
,我们抽象化一下:即 IR k x ---> add(k + 1, x)
。
3.在链表最右端插入结点
在最右端插入结点时,需要修改尾指针指向的结点的指针域。即 R ---> add(l[1], x)
。
4.在第 k 个插入的数左边插入结点
将在左边插入放到最后说明是因为这里有一个巧妙的处理,最好在熟练掌握插入思想后接触,即:在第 k 个结点的左边插入等价位在第 k-1 个结点的右边插入(要求第 k-1 个插入的结点是存在的状态)。同理我们要对 k-1 结点的指针进行修改。所以 IL ---> add(l[k + 1], x)
。
双链表的删除操作依旧很简洁。直接看代码
void remove(int k)
{
l[r[k]] = l[k]; // 将 k 结点 右指针指向的结点的左指针 设置为 k 左指针指向的结点
r[l[k]] = r[k]; // 将 k 结点 左指针指向的结点的右指针 设置为 k 右指针指向的结点
}
若第 k 次插入的结点存储在下标 k + 1,即D k ---> remove(k + 1)
。
#include
using namespace std;
const int N = 100010;
int e[N], l[N], r[N], idx;
void init()
{
// r[1] l[0] 恒为空
r[0] = 1; // head 向后指向 tail
l[1] = 0; // tail 向前指向 head
idx = 2;
}
// 都可以看作是在第 k 个结点右边插入一个结点
void add(int k, int x)
{
e[idx] = x; // 存储数据
l[idx] = k; // ① 当前结点的左指针指向第 k 个结点
r[idx] = r[k]; // ② 当前结点的右指针设为第 k 个结点的右指针指向
l[r[k]] = idx; // ③ 第 k 个结点右指针指向结点的左指针设置为当前结点
r[k] = idx; // ④ 第 k 个结点的右指针指向当前节点
idx ++;
}
void remove(int k)
{
l[r[k]] = l[k]; // 将 k 结点 右指针指向的结点的左指针 设置为 k 左指针指向的结点
r[l[k]] = r[k]; // 将 k 结点 左指针指向的结点的右指针 设置为 k 右指针指向的结点
}
int main()
{
int n;
cin >> n;
init();
while (n --)
{
string op;
cin >> op;
int k, x;
// 看作是在第 0 个插入的结点的右边插入一个结点
if (op == "L") {
cin >> x;
add(0, x);
}
// 修改尾指针指向结点的指针
else if (op == "R") {
cin >> x;
add(l[1], x);
}
//等价于在第 k - 1 个插入结点的右边插入
else if (op == "IL") {
cin >> k >> x;
add(l[k + 1], x);
}
// 最“标准的”插入操作
// 第 k 个插入结点的下标存储在 k + 1 的位置
else if (op == "IR") {
cin >> k >> x;
add(k + 1, x);
}
else if (op == "D"){
cin >> k;
remove(k + 1);
}
}
// i = r[i] 为不断指向右指针指向的结点
for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
}