单链表的实现

         这是C++算法基础-数据结构专栏的第十九篇文章,专栏详情请见此处


引入

        从这篇博客开始,我们就要正式学习数据结构了。

Q:什么是数据结构呢?

A:数据结构是在计算机中存储、组织数据的方式。小到变量、数组,大到线段树、平衡树,都是数据结构。

Q:为什么要使用数据结构呢?

A:程序运行离不开数据结构,不同的数据结构又各有优劣,能够处理的问题各不相同,而根据具体问题选取合适的数据结构,可以大大提升程序的效率。所以,学习各种各样的数据结构是很有必要的。

        而链表就是一种常见的数据结构,它分为单链表和双链表,它们的区别是指针的指向。

        下面我们就来讲单链表的实现。

定义

        链表是一种用于存储数据的数据结构,使数组如链条一般的方法连接元素。它的特点是插入与删除数据十分方便,但寻找与读取数据的表现欠佳。

        单向链表中包含数据域和指针域,其中数据域用于存放数据,指针域用来连接当前结点和下一节点。它的主要用途是储存邻接表(树与图的一种存储方式)。

过程

Tips:操作链表时,每个节点的指向的部分比较抽象,光靠文字描述和代码可能难以理解,建议配合作图来理解。

        链表的实现方式通常有两种,一种是结构体+指针,一种是定义多个数组实现,而比较容易理解和代码实现的则是第二种方法,所以我们这次就要讲解用数组实现的链表。

        单链表的最主要操作有三个:构建、插入(写入)数据和删除数据

        请注意:下面文字中“某一节点的下一个节点”和“某一节点所存储的下一个节点”是有区别的!“某一节点所存储的下一个节点”指的是这个节点的ne[]中存储的下标!

        构建单链表

        首先需要创建两个数组:e\left [ \right ]存储每个节点的值ne\left [ \right ]存储每个节点的下一个节点的下标,这两个数组是用下标关联起来的;再创建一个头结点head,刚开始它指向空节点(-1),链表中有节点后就指向第一个节点;其次还需要一个变量idx,表示当前节点的下标

        向链表中插入(写入)数据

        需要说明一点:链表中每个节点的下标不需要管顺序,也就是说,下标位3的节点不一定就在下标为5的节点前面。

        向k节点后插入一个节点的流程大致如下:

  1. idx节点的e\left [ \right ]中存储数值;
  2. idx节点的ne\left [ \right ]存储为k节点所存储的ne\left [ \right ]
  3. k节点的ne\left [ \right ]存储为idx

        首先,图一展示了起始状态;然后,在新节点上面储存数据(第一步,图二);由于新节点要插在k节点后,所以新节点的下一个节点要赋值为k节点所存储的下一个节点(第二步,图三);最后,k节点的下一个节点要赋值为idx节点(第三步,图四)。

        还有一点需要注意,操作完成后需要idx++,以便进行下一个节点的插入。

        除了向k节点后插入一个节点,也有向头节点后插入一个节点(简称头插)的操作,这两者虽然代码实现有所不同,但是思路大同小异,那头插的代码就不再展示了,留给读者去思考。(删除数据也有类似操作,后面也不再讲解)

Q:上文提到定义多个数组实现比较容易,它有什么优越性呢?

A:这种方式除了代码比较简短,容易理解,再就是插入(写入)数据(创建新节点)很快。

        在链表中删除数据

        将k节点后的节点删除,实际就是将k节点的ne\left [ \right ]存储为它所存储的ne\left [ \right ]所存储的ne\left [ \right ]

        首先,图一展示了起始状态;然后,k节点的下一个节点要赋值为k节点所存储的下一个节点所存储的下一个节点(第一步,图二)(这句话好像绕口令o((⊙﹏⊙))o)。

        细心的人可能能发现,在图中下标为2的节点,经过删除操作后,已经没有用了,但是还是占用着内存,这样会不会有问题呢?

        实际上,算法题目的内存限制很大,一般不需要管被删除的节点下标,在工程上我们才需要注意(这种情况是使用动态链表实现的,删除后就自动释放内存了)。

        我们可以看出,虽然空间浪费了,但是时间复杂度比自动释放内存的动态链表要快,这种方法称为空间换时间

性质

        这里,我们通过链表与数组之间的区别来理解链表的性质。

        链表和数组都可用于存储数据。与链表不同,数组将所有元素按次序依次存储。不同的存储结构令它们有了不同的优势:

        链表因其链状的结构,能方便地删除、插入数据,操作次数的时间复杂度是O(1)的。但也因为这样,寻找、读取数据的效率不如数组高,在访问数据中的时间复杂度是O(n)的。

        数组可以方便地寻找并读取数据,在访问数据中的的时间复杂度是O(1)的。但删除、插入的时间复杂度是O(n)的。

代码

        下面给出单链表的实现代码:

int head,e[N],ne[N],idx;

void init(){
    head=-1;
    idx=0;
}

void add(int k,int x){
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}

void remove(int k){
    ne[k]=ne[ne[k]];
}
        代码解释

        第一行的各种变量和数组已经在前面讲解了,这里不再详细讲解;init()函数的作用是初始化;add()函数的作用是将x插到下标是k的节点后面;remove()函数的作用是将下标是k的节点后面的节点删掉。


上一篇-    C++算法基础专栏文章    下一篇-


每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容

点个赞,关注一下呗~

  • 11
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值