数据结构—链表详解
浅谈数据结构——链表
本篇随笔就数据结构——链表进行讲解。链表是一种特别实用的数据结构,我把它理解为数组的升级版,也就是在数组的基础上,它能做到在任意位置添加或者删除元素,而不影响其他元素。链表还是我们进行图论学习时,图的常用存储方式——邻接表(链式前向星)的实现基础。学习链表需要读者具有一定的语法基础,最好会一点点指针。(不会也没关系,我们主要讲解数组模拟链表)
什么是链表
链表,顾名思义,就是带链的表。我已经说过,链表属于数组的加强版。那我们可以借助数组来理解链表:如果说数组是一长排连在一起的“方块”的话,那么链表就是把这些方块“拉开“,每个方块还有两个箭头,分别指向这个方块前面的方块和后面的方块。
这样我们就可以理解,为什么链表可以支持随机插入和删除了。从某种意义上来说,这里的每一个方块都是离散的,我们在某两点插入的时候,只需要把要插入的元素,这个元素目标位置前面的元素、后面的元素的箭头改一下,就做到了插入的操作。删除同理。
链表的实现原理
根据刚才的理解,我们可以发现,我们可以用一个结构体来模拟每一个方块,结构体中存一个元素和两个指针,指针分别指向上一个元素的位置和下一个元素的位置。但是蒟蒻不会指针指针的实现比较麻烦,而且在调试的时候也不是很理想。所以我们来想指针的本质就是告诉你一个位置,那么针对于”加强数组“链表来讲,这个位置可以用什么来表示呢?
对,数组下标。
所以我们刚才的结构体就可以简化,变成存一个元素和两个int变量(存储数组下标)。这样,我们就可以用结构体数组模拟链表的实现。
链表基本操作的代码实现
链表实现的精髓就是更改指针,改掉了三个元素(前,中,后)的指针使链表合法,就完成了我们需要做的操作,本部分不再就每段代码进行过多讲解,请大家自行理解代码含义,最好借助纸笔推演,看的会更明白一些。
(1)初始化
我们初始化链表的时候,要根据题目意思处理开头的第一个元素,这很重要!并且,我们把所有的指针都清成-1,这样保证了链表初始绝对合法。
void init()
{
for(int i=1;i<=n;i++)
a[i].pre=a[i].nxt=-1;
a[1].nxt=-1;
a[1].pre=0;
a[0].nxt=1;
}
(2)插入操作
void insert_left(int pos,int k)//把元素k插入到pos元素之前
{
a[a[pos].pre].nxt=k;
a[k].pre=a[pos].pre;
a[pos].pre=k;
a[k].nxt=pos;
}
void insert_right(int pos,int k)//把元素k插入到pos元素之后
{
a[a[pos].nxt].pre=k;
a[k].nxt=a[pos].nxt;
a[pos].nxt=k;
a[k].pre=pos;
}
(3)删除操作
void remove(int x)
{
if(a[x].pre==-1)
return;
a[a[x].pre].nxt=a[x].nxt;
a[a[x].nxt].pre=a[x].pre;
a[x].pre=-1;
a[x].nxt=-1;
}
(4)遍历
void print()
{
int start=a[0].nxt;
while(1)
{
printf("%d ",start);
if(a[start].nxt==-1)
break;
start=a[start].nxt;
}
}