文章目录
前言
数据结构的常见线性表,分别是顺序表,链表,栈,队列
本篇给大家带来单向无头链表的实现和讲解
为什么会有单链表?
- 动态顺序表储存数据存会造成
一定的消耗 - 链表存储数据是
按需扩容,不会造成空间浪费 - 通常做复杂数据结构的
子部分
等单链表的相关文章写完,之后就出一篇详细顺序表和单链表的区别
一、链表的概念和结构
1.1 概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
1.2 结构和存储
-
逻辑结构

-
物理结构

- 从上图可看出,链式结构在逻辑上是连续的,但是在物理上不一定连续,他们这种连续的结构主要是其指针域指向了下一个结点的地址。
- 现实中的结点一般都是从堆(heap)申请出来的。
- 从堆上申请空间,是按照一定的策略来分配的,两次申请的空间可以能连续,也可能不连续。比如连续:第一个结点地址是0x0023b11,下一个地址就是0x0023b15
二、链表的分类
2.1 单项和双向

2.2 带头和不带头

2.3 循环和非循环

综上所述以有8种链表,但是我们实际用的就**2种**

-
无头单向非循环链表:结构简单,一般不会单独用来存放数据。实际中更多是作为
其他数据结构的子结构。如:哈希桶、图的领接表等等。另外这种结构在笔试面试中出现很多。 -
带头双向循环链表:结构最复杂,一般用在单独存储数据。另外这个结构虽然复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
三、单链表的实现
一般我们写一个项目的时候,一共包含3个文件。
- 头文件.h:存放该项目所有需要的头文件,结构定义,函数声明。
- 函数定义文件.c:函数的定义和实现
- 测试文件.c:测试函数的功能
3.1 定义单链表的结构
一般定义结构前,我们会先重命名该结构的数据类型,因为这个结构可能会存放不止一种数据,可能会存放int、float、double等等,所以为了方便我们代码后期维护,重定义后只需要改变一处就可以了。
// 存放的数据类型
typedef int SListDataType;
单个节点包含两个部分:数据域(data)和指针域(next)

- data:存放结点的数据部分
- next:存放下一个结点(结构体)的地址
// 单链表结点的定义
typedef struct SListNode
{
SListDataType data; // 数据域
struct SListNode* next; // 指针域
}SListNode;

这里的意思就是重命名,把
sturct SListNode 命名为SListNode,大家可以看我们后面的代码我定义变量的使用,类型是SListNode,正常情况在c语言里是不行,必须要加
关键字struct+结构体名才可以定义,c++里面就不要加关键字。
3.2 创建一个结点
每次插入的时候,我们都需要写很多重复的代码,所以可以把创建节点写成一个函数,后面方便调用。
思路:
- 申请一块结点结构体大小的空间
- 对他进行判断,是否为NULL
- 把数据放到data
- next指向NULL
- 最后返回申请成功的结点
// 创建一个节点
SListNode* CreateSListNode(SListDataType x)
{
// 申请一块空间
SListNode* newNode = (SListNode*)malloc(sizeof(SListNode));
// 判断是否为空
if (newNode == NULL)
{
printf("new node falied!");
exit(-1);
}
// 需要插入的数据放到data里
newNode->data = x;
// 该结点的next指向空
newNode->next = NULL;
// 返回申请的结点
return newNode;
}
3.3 尾插

思路:
- 利用assert,防止实参传过来NULL,这是不是说链表地址,而是直接传NULL
- 调用刚才创建结点的函数
- 判断链表是否为空,分两种情况,1. 链表为空 2. 链表不为空
为空就直接把创建的结点设为头节点,不为空就找尾,把尾结点的next指向新创建的结点

// 尾插
void

本文详细介绍了单链表的概念、结构,包括其分类(单向、双向、带头、不带头、循环、非循环),并实现了各种操作如头插、尾插、查找、删除和销毁,同时分析了pos处和pos后面操作的时间复杂度特点。
最低0.47元/天 解锁文章
1877






