链表对应的英文名称是 Linked List,是一种物理存储单元上非连续、非顺序的数据结构,由若干个节点(node)组成。每个结点包括两个部分:一个是存储数据元素的数据域(data),另一个是存储下一个结点地址的指针域(next)。

链表的第一个节点被称为头结点,最后一个节点被称为尾结点,尾结点的next指针指向一个空地址 NULL。

不同于数组的顺序存储,链表在内存中是随机存储。

可以说,循环链表是一种特殊的单链表。它与单链表的唯一区别在于尾结点,循环链表的尾结点指针指向链表的头结点。当要处理的数据具有环形结构特点时,特别适合采用循环链表。例如,约瑟夫问题。

双向链表它的每一个节点除了有数据(data)、后继指针(next),还有前驱指针(prev)——指向前一个节点的指针。由于多了一个前驱指针所以双向链表要更占内存,但它的效率更高也更加实用(支持双向遍历),可以说是“空间换时间”了。

对于第1种情况,无论是单向链表还是双向链表,都需要从头节点开始遍历查找,查找到后,再执行删除操作。

对于第2种情况,因为双向链表中拥有前驱指针,只需要O(1)的时间复杂度;而单向链表为了找到前驱节点需要遍历,则需要O(n)。同理,对于“在某个指定节点前插入一个节点”这种情况,双向链表也是O(1),单向链表也是O(n)。除此之外,对于一个有序链表,双向链表的按值查询的效率也比单向链表高一些。

相较于数组,链表的内存空间消耗更大,用于储存结点指针信息。

链表可以克服数组需要预先知道数据大小的缺点,充分利用计算机内存空间,实现灵活的内存动态管理。但是(单向)链表失去了数组根据下标随机读取的优点,想要找到某节点Z只能通过前一个节点Y的next指针来寻找,而想要找到节点 Y,又必须通过Y节点的前一个节点X的next指针来寻找...要找一个某一节点,必须要从头开始遍历,十分麻烦。链表的随机访问的性能没有数组好,需要O(n)的时间复杂度。

数组在进行插入、删除操作时,为了保持内存数据的连续性,需要搬移大量数据,所以时间复杂度为O(n);而由于不必须按顺序存储,链表在纯粹的插入、删除操作的时候可以达到O(1);而如果考虑插入、删除操作之前需要遍历查找到元素,那么删除“特定值”操作也需要O(n)。

由于链表的每个结点在内存里都是随机分布的,只是通过指针联系在一起,所以这些结点的地址并不相邻,无法利用 程序的局部性原理 来提前加载到缓存中来提升程序性能。所以,链表的查找、读取性能不及数组。如果数据以查找为主,很少涉及到增加和删除操作,那么选择数组,如果涉及到频繁的插入和删除,或元素所需分配空间过大,那么倾向于选择链表。