【第五课】数据结构:双链表(acwing827)(含思路详解 c++代码)

写在前面 

好久没学算法了哎,继续咯。可以慢但要坚持哦。

数组模拟单链表:👇

http://blog.csdn.net/Swillow_/article/details/132154952?spm=1001.2014.3001.5502

在上次的文章中,我们了解了数组模拟单链表,解释了结构体链表的弊端以及数组模拟链表的优势,那接下来理解双链表就容易些咯~


思路

如上图,插入、删除节点的直接思路就比较容易想到了,很直接也好理解。注意需要修改的指向即可,细心一点一般没问题哒。

代码思路

准备工作 

确定了大体的思路,我们就要思考如何用代码来实现,毕竟我们知道有思路和写出来写对还是有距离的hhh。

首先先明确我们需要的数组。最先想到的毫无疑问肯定是需要实在用来存储数据的数组,为了和单链表里保持一致我们更有熟悉感,这里我还用 e[] 来对实际存储数据的数组命名;接着在结构体双链表中,节点结构体中包含着实际数据,前一个数据的地址和后一个数据地址,这三个元素,由此我们可以想到,需要用两个数组 l[] r[] 来存储需要的地址(前一个和后一个用 l r 感觉也挺形象的嘞),注意哦,数组本身存储的都是数,我们这里为了模拟链表,这两个数组存储的是数字,但是其表示的含义是地址序号哦,这里要搞清楚。

接着要写一个初始化的空链表。这里我们偷个懒,为避免一些需要额外考虑的边界问题,我们把链表的起始下标设为0,末尾设为1(这里后面详细解释哦),这样的话我们的地址下标idx就应该是从2开始咯。

ok啦,准备工作都做好之后,开始写主函数咯。

核心函数

一些主要函数之外的老套的写法就不再解释啦,写过很多遍啦,相信大家都可以很容易写出来了。我这里主要对插入和删除这两个核心函数解释哦。

插入

对了,在题目要求里,好像有很多不同的操作,注意他在迷惑你hhh(神经hhh),其实这些各式各样的花式插入都可以用一个在第k个元素后插入节点来表示。

最左端插入:也就是在起始元素即地址下标为0的元素后插入。这个传参可以直接写0即可

最右段插入:也就是在末尾插入即地址下标为1的元素前(左边)插入。代码参数表示可以写成l[1]

如上面的图,思路主要就是修改几个地址指向即可。一些解释和注意点都写在注释里啦。相信大家可以理解哒。

// 在第k位的右侧插入一个值
void Insert(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];// 更新 我 右边元素 的 左侧指向
}

注意!!

1.初始化链表:

我们需要单独写一个函数进行链表的初始化。

这里注意,为了避免一些边界问题:在进行链表操作(如插入、删除节点等)时,需要特殊处理头节点和尾节点的情况。如果我们不预先设定起始和末尾节点,那么在进行这些操作时,就需要额外判断当前节点是否为头节点或尾节点,这会增加代码的复杂性。因为不能确定当前已知节点是否是头节点或尾节点,我们需要多一些步骤去判断寻找头/尾节点。

我们通常会将链表的起始下标设为0,末尾设为1,确定了两个下标来作为链表的边界点,注意这里的两个下标只表示占了两个地址位,其并没有实际的数据

所以起始输入实际数据的地址位idx应该从2开始

2.定义操作指令

这里应该定义为string,字符串类型。因为在题目要求中,有超过一个字符的指令,为了方便直接string咯。那么下面if条件句就要注意双等号和双引号哦。

3.传参

由于我们用一个 在第k个插入的数后面插入一个数,所以我们想表示第k个数,就像之前单链表解释过的一样,第k个插入的数地址标为k+1.注意别写错哦

末尾插入记得表示为向最后一个地址符的右边插入,即 l[1].

同样,在第k个数左侧插入,也是表示为向第k个数左边的数的右边插入,即 l[k+1]

代码如下

#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int m, x, k, idx;
int l[N], r[N], e[N]; // 数组 l 和 r 表示的是地址索引而不是具体的数值
void init()
{
    l[1] = 0; // 第0位为 起始位置
    r[0] = 1; // 第1位为 末位
    // 仅表示边界,并没有具体的数值
    idx = 2;
}
// 在第k位的右侧插入一个值
void Insert(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()
{
    init();
    cin >> m;
    string order;
    while (m--)
    {
        cin >> order;
        if (order == "L") // 从最左侧插入也就是在第0位的右边插入
        {
            cin >> x;
            Insert(0, x);
        }
        else if (order == "R") // 从最右侧插入即在第1位的左边插入
        {
            cin >> x;
            Insert(l[1], x);
        }
        else if (order == "D")
        {
            cin >> k;
            remove(k + 1); // 第k个插入的数下标为k+1
        }
        else if (order == "IL")
        {
            cin >> k;
            cin >> x;
            Insert(l[k + 1], x);
        }
        else if (order == "IR")
        {
            cin >> k;
            cin >> x;
            Insert(k + 1, x);
        }
    }

    for (int i = r[0]; i != 1; i = r[i])
    {
        cout << e[i] << " ";
    }
}

这段代码对于题目中的测试样例是可以过的。也测试了很多其他数据也是没问题的,但下面这个一直过不了,我暂时还不理解,之后明白了再来修改。 当然求指点!!

7 
L 10
R 20
D 1
IL 1 30
IR 1 40
L 50
R 60

正确的输出应该是 50 30 20 40 60(我没算错的话) ,但是我去csdn上找别人写的代码,输出也是 50 30 20 60,不理解hhh


有问题欢迎指出,非常感谢!!!

也欢迎交流和建议哦。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值