链表(上)

Posted hardyyao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了链表(上)相关的知识,希望对你有一定的参考价值。

链表是什么?

来看看维基百科给出的定义:

  • 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存储到下一个节点的指针(Pointer)。
  • 由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比数组快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

链表和数组在内存中的分布,大概是这样的:

技术分享图片

简单点来说,链表和数组一样是一种线性表的数据结构,不同之处在于,定义一个数组需要申请一块连续的存储空间来存储;而链表恰恰相反,他并不需要一块连续的内存空间,而是在每一个节点里存储到下一个节点的指针,通过“指针”将一组零散的内存块串联起来使用。

常见的链表结构有哪些?

  • 单链表
  • 循环链表
  • 双向链表

单链表

单链表是最简单、最常用的链表结构。

每个链表的节点除了存储数据之外,还需要记录链上的下一个结点的地址。如图所示,这个记录下个结点地址的指针叫作后继指针 next。

技术分享图片

图中其中有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。

循环链表

循环链表是一种特殊的单链表。它跟单链表唯一的区别就在尾结点,循环链表的尾结点指针是指向链表的头结点。如下图所示:

技术分享图片

双向链表

双向链表在实际的软件开发中也很常用,其结构如下图所示:

技术分享图片

双向链表需要额外的两个空间来存储后继结点和前驱结点的地址。所以,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间。

但双向链表可以支持双向遍历,它可以支持 O(1) 时间复杂度的情况下找到前驱结点,正是这样的特点,也使双向链表在某些情况下的插入、删除等操作都要比单链表简单、高效。

双向循环链表

把循环链表和双向链表结合起来,就是双向循环链表,如下图所示:

技术分享图片

链表和数组各种操作时间复杂度对比

技术分享图片

数组简单易用,在实现上使用的是连续的内存空间,可以借助 CPU 的缓存机制,预读数组中的数据,所以访问效率更高。而链表在内存中并不是连续存储,所以对 CPU 缓存不友好,没办法有效预读,访问效率较低。

数组的缺点是大小固定,而且需要一块连续的内存空间。相比起来,链表则较为灵活,没有大小限制,天然支持动态扩容。

内容小结

  • 链表是一种非连续性的线性表数据结构。
  • 和数组相比,链表更适合插入、删除操作频繁的场景。
  • 链表查询的时间复杂度较高,不适用于查询操作频繁的场景。

以上是关于链表(上)的主要内容,如果未能解决你的问题,请参考以下文章

单向链表上是否有环

Java连载83-单向链表双向链表collections常用方法

链表(上)

线性表--03---链表----单向链表双向链表

合并两个有序的链表使新链表依然有序(不开辟新空间,在原链表上操作。 递归版本)

恋上数据结构 链表(手写LinkedList+练习)