一、链表的由来
数组是最简单的数据结构,链表复杂一些,二叉树、图则更加复杂的数据结构。数据结构由简单到复杂,它们所要解决的问题也是由简单到复杂。要学习复杂的数据结构就要先学习简单的数据结构,如果简单的数据结构可以解决问题,就没必要使用复杂的数据结构。数组天生的缺陷导致的它解决不了某些问题,所以人们发明了链表。
数组的三个特点:
一:数组中所有的元素类型必须相同;
二:数组在定义的时候需要明确指定数组元素的个数,并且一般来说个数是不能改变的(Linux内核中会使用变长数组,在高级语言如C++当中也支持变成数组);
三:某个元素的移动可能造成元素的大面积移动,效率不高。这些特点,也使得数组有了简单易用的好处,但是同样也带来了缺陷。那如何弥补这些缺陷?
一:数组的第一个缺陷靠结构体解决。结构体允许其中的元素类型不同。
二:数组的第二个缺陷有两个解决思路:1、使用变长的数组。2、使用链表。
三:针对第三个缺陷使用链表是最好的解决方法。
二、单链表实现
单链表的创建到使用大致有如下几步:
1、创建空的单链表,比如可以定义一个CreatNode()函数来创建第一个节点。
2、操作单链表(增、删、查、更改、排序),比如说插入操作,定义一个InsertTail()函数来向单链表(或是节点)的后面追加新节点;
3、销毁单链表,比如定义一个DestroyList()函数,用于销毁链表。
1、构建第一个节点
单链表的节点构成
在C语言中节点的创建方法就是定义一个结构体。
struct node
{
int data;
struct node* pNext;
};
- 注意:这里只是定义了一个结构体类型,本身并没有变量的生成,故不占用内存(比如说数据类型 int、char 、float,它们是不占用内存空间)。
使用堆内存创建一个节点
为什么要使用对内存?用来形成链表的内存一般有如下特点:必须需要多少有多少;必须可以随意删除和释放。根据上面两个特点,我们就知道堆内存是最适合用来做链表的节点的。
下面是创建节点函数的伪代码:
CreatNode(节点中要存的数据)
{
申请一个节点大小的堆内存;
检查堆内存是否申请成功;
清理申请到的堆内存;
填充节点的数据;
节点中指针初始化为NULL;
}
下面是伪代码的实现过程:
//创建一个新节点
typedef struct node StrNode_T;
StrNode_T* CreatNode(int data)
{
StrNode_T* p = (StrNode_T*)malloc(sizeof(StrNode_T));
if (NULL == p)
{
printf("malloc 申请失败!!!!"