单链表中,在C语言中用结构指针来描述。
假设 p 是指向线性表第 i 个元素的指针,
p -> data 表示结点 ai 的数据域。
p -> next 表示结点 ai 的指针域,p -> next 的值是一个指针, 指向第 i + 1个元素。即指向 ai+1的指针。
则如果 p -> data = ai ,那么 p -> next -> data = ai+1
单链表的读取
在线性表顺序存储结构中,计算任意一个元素的存储位置较为容易。?
对于单链表实现获取第 i 个元素的数据的操作GetElem, 在算法上,相对要麻烦一些。
获得链表第 i 个数据的算法思路::
- 声明一个结点 p 指向链表第一个结点,初始化 j 从 1 开始;
- 当 j < i 时, 就遍历链表,让 p 的指针向后移动,不断指向下一结点, j 累加 1;
- 若到链表末尾 p 为空, 则说明第 i 个元素不存在;
- 否则查找成功,返回结点 p 的数据。
实现代码算法如下:
即从头开始找,直到找到第 i 个元素为止,因此最坏情况的时间复杂度为 O(n)。
由于单链表的结构中没有定义表长, 所以不能事先知道要循环多少次,因此也就不方便使用for 来控制循环。主要核心“工作指针后移”。
单链表的插入与删除
-
单链表的插入:
假设存储元素 e 的结点为 s ,要实现结点 p ,p -> next 和 s 之间逻辑关系的变化, 只需要将结点 s 插入到结点 p 和 p -> next 之间即可。
只需要让p -> next 和 s -> next 的指针做一点改变即可:s->next = p->next ; p->next = s 。 即让 p 的后继结点改成 s 的后继结点,再把结点 s 变成 p 的后继结点。
两句的顺序不可以交换。插入结点 s 后,链表如图所示。
第 i 个数据插入结点的算法思路:
- 声明一结点 p 指向链表第一个结点, 初始化 j 从 1 开始;
- 当 j < i 时, 就遍历链表,让 p 的指针向后移动,不断指向下一结点,j 累加 1 。
- 若到链表末尾 p 为空,则说明第 i 个元素不存在;
- 否则查找成功, 在系统中生成一个空结点 s ;
- 将数据元素 e 赋值给 s -> data;
- 单链表的插入标准语句 s -> next = p -> next; p -> next = s;
- 返回成功。
实现代码:
单链表的删除
设存储元素 ai 的结点为 q, 要实现将结点 q 删除单链表的操作 ,其实就是将它的前继结点的指针绕过,指向它的后继结点即可,
单链表第 i 个删除结点的算法思路:
- 声明一结点 p 指向链表第一个结点, 初始化 j 从 1开始;
- 当 j < i 时 ,就遍历链表, 让 p 的指针向后移动,不断指向下一结点, j 累加 1。
- 若到链表末尾 p 为空,则说明第 i 个元素不存在;
- 否则查找成功,将欲删除的结点 p -> next 赋值给 q;
- 单链表的删除标准语句 p -> next = q - > next;
- 将 q 结点中的数据赋值给 e ,作为返回;
- 释放 q 结点;
- 返回成功
实现代码:
从整个算法来说,我们很容易推导出:它们的时间复杂度都是 O(n),如果在我们不知道第 i 个元素的指针位置,单链表数据结构在插入和删除操作上,与线性表的顺序存储结构是没有太大优势的。但如果,我们希望从第 i 个位置,插入10 个元素 ,那么单链表的效率优势就会非常明显。
单链表的整表创建
创建一个单链表的过程就是一个动态生成链表的过程。即从“空表”的初始状态起,依次建立各元素结点 ,并逐个插入链表。
单链表整表创建思路:
- 声明一结点 p 和计数器变量 i ;
- 初始化一空链表 L ;
- 让 L 的头结点的指针
- 循环:
-生成一新结点赋值给 p;
-随机生成一数字赋值给 p 的数据域 p -> data;
-将 p 插入到头结点与前一新结点之间。
实现代码算法如下:
- 上图是使用插队的方法,始终让新结点在第一的位置,即头插法。
- 也可以把新结点都插在终端结点的后面,尾插法。
L是指整个单链表,而 r 是指向尾结点的变量, r 会随着循环不断地变化结点,而 L 则是随着循环增长为一个多结点的链表。
单链表的整表删除
删除算法思路:
- 声明一个结点 p 和 q;
- 将第一个结点赋值给 p ;
- 循环:
-将下一个结点赋值给 q ;
-释放 p;
-将 q 赋值给 p;
单链表结构与顺序存储结构优缺点
- 若线性表需要频繁查找,很少进行插入和删除操作是,宜采用顺序 存储结构。
- 若需要频繁插入和删除时,宜采用单链表结构。比如说游戏开发中,对于用户注册的个人信息,除了注册时插入数据外,绝大多数情况都是读取,所以考虑用顺序存储结构。而游戏玩家的武器或装备列表,随着游戏过程,可能会随时增加或删除,此时可以用单链表结构,(仅作为举例)。
- 当线性表中的元素个数变化比较大或者根本不知道有多大时,最好用单链表结构,这样可以不需要考虑存储空间的大小问题。而如果实现知道大致长度,用顺序存储结构效率会高很多。