链表

文章目录

概念

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。

元素可以存储在内存中的任何地方。链表中的每个元素都存储下一个元素的地址,从而使一系列随机的内存地址串在一起。

链表由一系列节点(链表中每一个元素称为节点)组成,节点可以在运行时动态生成。每个节点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个节点地址的指针域。

生活模型:藏宝游戏

优点

只要有足够的内存,就可以为链表分配内存。

缺点

假如你想读取最后一个元素,不能直接读取。但是你不知道它所在的地址,必须从头开始访问,直到访问到最后一个元素。同时访问所有元素时,效率很高,单独访问时,因为需要跳跃,导致效率很低。

复杂度比较

数组 链表
读取 O(1) O(n)
插入 O(n) O(1)
删除 O(n) O(1)

需要插入元素时,若使用链表,只需修改前面元素指向的地址;而如果使用数组,则必须把后面所有元素都向后移一位,如果内存空间不足,整个数组都得复制到其他地方。(类似排队)

因为数组支持随机访问,所以经常被用来应用在构建需要支持能够随机访问的数据结构中。

结构模型

链表的每个节点结构如下:
data|next
完整结构如下:
单链表
其中,head 保存首位节点的地址,其余节点保存本节点数据以及下一节点地址,最后一个节点保存节点数据,指向 null;

代码实现

  1. 使用 3 个指针遍历单链表,逐个链接点进行反转

  2. 从第 2 个节点到第 N 个节点,依次逐节点插入到第 1 个节点(head 节点)之后,最后将第一个节点挪到新表的表尾。
    原来的链表头最后移动到表尾

  3. 递归(相信我们都熟悉的一点是,对于树的大部分问题,基本可以考虑用递归来解决。但是我们不太熟悉的一点是,对于单链表的一些问题,也可以使用递归。可以认为单链表是一颗永远只有左(右)子树的树,因此可以考虑用递归来解决。或者说,因为单链表本身的结构也有自相似的特点,所以可以考虑用递归来解决)

    查找链表中间节点

    参见linked_list.find_middle_node()方法
    思路
    解法

判断回文

基本思路

  1. 把链表先拆分成 left 和 right 两部分,然后只对 right 部分进行逆序;
  2. 从 left 头节点和逆序后的 right 头节点开始遍历,一直往后遍历直到链表尾;
  3. 遍历过程中出现不相同的数据则不是回文的单链表,否则就是回文的单链表;

    图解

    1
    2
    3
    4

代码实现

回文判断

双向链表

概念

双向链表,又称为双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。当此节点为第一个节点时,前驱指向空值,后继指向下一个节点,最后一个节点反之。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

结构模型

指针域|数据域|指针域
完整结构如下
双向链表

代码实现