数据结构(二):链表

162 阅读3分钟

1、链表

1.1、链表定义

链表(linked list)是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。

链表又分为单向链表、双向链表:

  • 单向链表的每一个节点又包含两部分,一部分是存放数据的变量data,另一部分是指向下一 个节点的指针next。

Snipaste_2022-03-24_22-08-19.png

public class Node{
    int data;
    Node next;
}

链表的第1个节点被称为头节点,最后1个节 点被称为尾节点,尾节点的next指针指向空。

与数组按照下标来随机寻找元素不同,对于链表的其中一个节点A,我们只能根据节点A的 next指针来找到该节点的下一个节点B,再根据节点B的next指针找到下一个节点C。

  • 双向链表比单向链表稍微复杂一些,它的每 一个节点除了拥有data和next指针,还拥有指向前置节点的prev指针。

Snipaste_2022-03-24_22-08-19.png 链表在内存中的存储方式则是随机存储

什么叫随机存储呢?

链表则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。 这样可以灵活有效地利用零散的碎片空间。

Snipaste_2022-03-24_22-08-19.png 箭头代表链表节点的next指针。

1.2、链表的基本操作

1.2.1、查找节点

在查找元素时,链表不像数组那样可以通过 下标快速进行定位,只能从头节点开始向后一个 一个节点逐一查找。 Snipaste_2022-03-24_22-08-19.png 查找过程,如下:

将查找的指针定位到头节点。

Snipaste_2022-03-28_22-30-19.png 根据头节点的next指针,定位到第2 个节点。

Snipaste_2022-03-28_22-30-19.png 以此类推.....

查找链表节点的时间复杂度是:

最坏的时间复杂度是O(n)。

1.2.2、更新节点

如果不考虑查找节点的过程,链表的更新过程会像数组那样简单,直接把旧数据替换成新数据即可。

1.2.3、插入节点

分为3种情况:

  • 尾部插入 是最简单的情况,把最后一个节点的next指针指向新插入的节点即可。

Snipaste_2022-03-28_22-30-19.png

  • 头部插入 首先把新节点的next指针指向原先的头节点,然后把新节点变为链表的头节点。

Snipaste_2022-03-28_22-30-19.png

  • 中间插入 首先新节点的next指针,指向插入位置的节点,然后插入位置前置节点的next指针,指向新节点。

Snipaste_2022-03-28_22-30-19.png 只要内存空间允许,能够插入链表的元素是 无穷无尽的,不需要像数组那样考虑扩容的问题。

1.2.4、删除元素

  • 尾部删除 是最简单的情况,把倒数第2个节 点的next指针指向空即可。

Snipaste_2022-03-28_22-30-19.png

  • 头部删除 把链表的头节点设为 原先头节点的next指针即可。

Snipaste_2022-03-28_22-30-19.png

  • 中间删除 把要删除节点的前置节点的next指针,指向要删除元素的下一个节点即可。

Snipaste_2022-03-28_22-30-19.png 链表的插入和删除操 作,时间复杂度分别是:

不考虑插入、删除操作之前查找元素的过程,只考虑纯粹的插入和删除操作,时间复杂度都是O(1)。

1.3、数组和链表的选择

Snipaste_2022-03-28_22-30-19.png 根据上图可知,

  • 数组的优势在于能够快速 定位元素,对于读操作多、写操作少的场景来数组更合适一些。

  • 链表的优势在于能够灵活地进行插 入和删除操作,如果需要在尾部频繁插入、删除元素,用链表更合适一些。