AcWing 827. 双链表

题目描述


分析:

这里依然是用数组来模拟双链表。双链表中有一个头指针 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;
}

在这里插入图片描述

插入

初始化后就可以对链表进行插入结点了,有四种插入方法:

  1. 在第 k 个插入的数右边插入结点
  2. 在第 k 个插入的数的左边插入结点
  3. 在链表最右端插入结点
  4. 在链表最左端插入结点

首先要说的是这四个插入位置用的是同一个函数!

// 都可以看作是在第 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)

在这里插入图片描述


代码(C++)

#include <iostream>

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] << ' ';
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值