写在前面
好久没学算法了哎,继续咯。可以慢但要坚持哦。
数组模拟单链表:👇
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
有问题欢迎指出,非常感谢!!!
也欢迎交流和建议哦。