单链表(配图详解每一个函数接口)(上)

简介: 单链表(配图详解每一个函数接口)(上)

前言:Hello!大家好,我是@每天都要敲代码,上一期给大家带来了线性表之===》顺序表,没有学会的小伙伴可以再去学习一下吧!传送门 接下来我们先分析一下顺序表:


顺序表问题:

问题1:中间/头部的插入删除,都要进行数据的移动,时间复杂度为O(N)。

问题2:增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。

问题3:增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:

如何解决以上这种问题呢?下面给出了链表的概念和结构来看看。


概念和结构:


概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。


结构:

b7248f1c76654e809f6f9e8855aa3b16.png


种类

实际中要实现的链表的结构非常多样,以下情况组合起来就有gif.gif=8种链表结构:

1. 单向、双向

2. 带头、不带头

3. 循环、非循环


cadac91b463d47ef9011e70ab509c48b.png

37137a3915b740d4b533da3d879c36f8.png


4bf70b42577345fda2b79c7855cf571e.png


虽然有这么多的链表的结构,但是我们实际中最常用还是两种结构:


82e16e31676e4bf2bf67da9ed25bf445.png


无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结

构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都

是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带

来很多优势,实现反而简单了。


有了前面这些概念和结构,相信大家对链表已经有了初步的理解和印象,接下来让我们来一起学习无头单向非循环链表:


无头单向非循环链表:


1.创建单链表:SListNode


单链表创建的结构和顺序表是很相似的,这里我们只需要两个参数,一个data用来存储数据;一个next指针,用来指向下一个地址;并且我们用的是无头单向非循环链表,所以我们才开始的头指针plist初始化为NULL!那如果是带头结点的单链表呢?此时的plist就需要初始化成为一个malloc开辟的地址,这个等我们下一期讲到带头双向循环链表就会用到,这里就不在赘述。


ab440c68902f4c37907791b01f4d74fd.png


2.尾插函数的实现:SListNodePushBack

动态开辟空间的函数:SListNodeCreat


在这里的无论是插入还是删除,我们都可以去画图理解,这样也能防止写错。我们每次的插入都需要malloc动态开辟一个空间,所以不妨直接封装成一个函数SListNodeCreat,便于后面的使用,并且我们在开辟空间时,如果能开辟成功了,就把newnode给赋值上:newnode->data = x  newnode->next = NULL,下面看动态开辟空间的函数实现。


具体代码如下:



129f3b14f4414d6f983ce079b7e68ac1.png


动态开辟空间成功后,我们就开始尾插,这时就需要我们画图去理解了:


情况1:链表里本身就有很多数据,我们这时只需要找到尾结点(暂定为tail),把tail->next=NULL改成tail->next = newnode就可以,至于newnode->next=NULL就不需要写了,因为我们开辟空间时就已经让newnode->data=x和newnode->next=NULL了,下面看具体逻辑图:


97ea760bc5704a1c9d009267f1df861e.png


情况2:像我们现在刚开始使用的的一样,才开始就是一个空链表,我们第一次就进行了尾插,此时的*pphead和tail肯定都为空指针,这时我们只需要把才开辟的空间newnode赋值给*pphead就可以了,具体逻辑图:

1392ab861566421da6738bf35e697352.png


所以尾插时我们要考虑这两种情况,具体代码如下:


88bb58f27aee4f2fbc4e2edf2f1c067b.png

尾插几个数据我们先打印验证一下,具体打印函数下面就会给出:


721b795814a24b199205f51899b3a2e3.png

3.打印函数的实现:SListNodePrint

打印函数也很简单,这里就不在多说,直接给出代码:


152e5cb351694f9bb1e37be8404e24eb.png


4.头插函数的实现:SListNodePushFront


   对于头插函数就很简单了,它不像顺序表那样还需要移动后面的元素,所以无论是空链表还是有元素的链表都是一样的;这里通过指针直接链接,下面看具体逻辑图:


55e34ff381604a7485c7e09fb3782ec7.png

下面看具体代码:

6e09be20461947bb8965d67a1e5861ed.png

我们不妨头插一个0,进行打印验证:

993558326943423288e5162dcaf28056.png

之后我们也可以屏蔽掉前面尾插的函数,直接对空表进行头插,进行验证:


6ff597d33cf14b468e0d42e0f2b8a019.png


5.尾删函数的实现:SListNodePopBack


   对于尾删函数的实现可能就比较麻烦了,我们还是需要分情况讨论,先从最普通的情况进行画图分析,然后在把哪些特殊的情况单独拿出来分析:


情况1:链表里本身有很多元素,我们进行尾删时怎么办呢?肯定需要找到尾指针tail,因为我们malloc动态创建的,最后不使用了肯定要free释放掉;那还需要知道什么呢?我们还需要把最后一个指针的next置为空啊!我们都已经释放掉尾指针tail怎么找它前面一个指针prev把它的next置位空呢?所以我们还需要一个指针prev记住tail的前一个指针,下面看逻辑图:


6ec6cd32e54848b7b65af14513d83b50.png


情况2:如果是空链表呢?我们直接return退出不就好了,就不需要我们去删除了!

情况3:如果只有1个节点呢?尾结点就是头结点,尾结点tail前面根本没有一个节点prev怎么办呢?我们就直接释放掉当前的节点,然后置空就可以了:


dad7e7d9efc847f88972aba453e07e92.png

下面看具体代码:


378bd5cfb6e54822b40f6a5dd5f39284.png

不妨尾删一个数据测试结果:


a2f06328a9264ae3ac3ce164a58f3b5e.png

相关文章
|
4月前
|
C语言
【C语言刷题系列】删除公共元素
【C语言刷题系列】删除公共元素
|
10月前
|
存储 Java C++
数据结构单链表之C 中的通用链表 | 第十六套
数据结构单链表之C 中的通用链表 | 第十六套
54 1
|
10月前
|
算法
数据结构单链表之以给定大小的组反转链表 | 第十二套
数据结构单链表之以给定大小的组反转链表 | 第十二套
35 0
|
10月前
|
存储 算法
数据结构单链表之检测和删除链表中的循环 | 第十三套
数据结构单链表之检测和删除链表中的循环 | 第十三套
48 0
|
10月前
|
存储
数据结构单链表之旋转链表 | 第十五套
数据结构单链表之旋转链表 | 第十五套
54 0
|
存储 算法
学C的第二十六天【指针的进阶(二)】
6 . 函数指针数组 (1). 含义: 函数指针数组 是一个数组,是用于存放 函数指针 的数组。 例如:
|
存储 编译器 C语言
《C和指针》读书笔记(第八章 数组)(下)
《C和指针》读书笔记(第八章 数组)
|
C语言 C++
《C和指针》读书笔记(第八章 数组)(上)
《C和指针》读书笔记(第八章 数组)(上)
|
Java 索引 Python
Python 触“类”旁通3|单链表基本操作之找查、删除和反转
Python 触“类”旁通3|单链表基本操作之找查、删除和反转
111 0