链表(单链表)是什么
链表又称单链表、链式存储结构,用于存储逻辑关系为“一对一”的数据。
和顺序表不同,使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。例如,使用链表存储 {1,2,3},各个元素在内存中的存储状态可能是:

可以看到,数据不仅没有集中存放,在内存中的存储次序也是混乱的。那么,链表是如何存储数据间逻辑关系的呢?
链表存储数据间逻辑关系的实现方案是:为每一个元素配置一个指针,每个元素的指针都指向自己的直接后继元素,如下图所示:

显然,我们只需要记住元素 1 的存储位置,通过它的指针就可以找到元素 2,通过元素 2 的指针就可以找到元素 3,以此类推,各个元素的先后次序一目了然。
像图 2 这样,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系,这样的存储结构就是链表。
结点(节点)
很多教材中,也将“结点”写成“节点”,它们是一个意思。
在链表中,每个数据元素都配有一个指针,这意味着,链表上的每个“元素”都长下图这个样子:

数据域用来存储元素的值,指针域用来存放指针。数据结构中,通常将图 3 这样的整体称为结点。
也就是说,链表中实际存放的是一个一个的结点,数据元素存放在各个结点的数据域中。举个简单的例子,图 2 中 {1,2,3} 的存储状态用链表表示,如下图所示:

在 C 语言中,可以用结构体表示链表中的结点,例如:
我们习惯将结点中的指针命名为 next,因此指针域又常称为“Next 域”。
头结点、头指针和首元结点
图 4 所示的链表并不完整,一个完整的链表应该由以下几部分构成:
头指针:一个和结点类型相同的指针,它的特点是:永远指向链表中的第一个结点。上文提到过,我们需要记录链表中第一个元素的存储位置,就是用头指针实现。
结点:链表中的节点又细分为头结点、首元结点和其它结点:
头结点:某些场景中,为了方便解决问题,会故意在链表的开头放置一个空结点,这样的结点就称为头结点。也就是说,头结点是位于链表开头、数据域为空(不利用)的结点。
首元结点:指的是链表开头第一个存有数据的结点。
其他节点:链表中其他的节点。
也就是说,一个完整的链表是由头指针和诸多个结点构成的。每个链表都必须有头指针,但头结点不是必须的。
例如,创建一个包含头结点的链表存储 {1,2,3},如下图所示:

再次强调,头指针永远指向链表中的第一个结点。换句话说,如果链表中包含头结点,那么头指针指向的是头结点,反之头指针指向首元结点。
链表的创建
创建一个链表,实现步骤如下:
定义一个头指针;
创建一个头结点或者首元结点,让头指针指向它;
每创建一个结点,都令其直接前驱结点的指针指向它。
例如,创建一个存储 {1,2,3,4} 且无头节点的链表,C 语言实现代码为:
再比如,创建一个存储 {1,2,3,4} 且含头节点的链表,则 C 语言实现代码为:
链表的使用
对于创建好的链表,我们可以依次获取链表中存储的数据,例如:
0 1 2 3 4
如果不想输出头结点的值,可以将 p->next 作为实参传递给 display() 函数。
如果程序中创建的是不带头结点的链表,最终的输出结果应该是:
1 2 3 4