数据结构:链表

发布于:2025-07-04 ⋅ 阅读:(16) ⋅ 点赞:(0)

1. 链表

1.1 链表的概念

我们知道顺序表的缺点很明显,那就是当频繁的插入和删除时,会导致大量的频繁的移动元素,这显然就会消耗时间。

那么链表就很好的解决了这个问题,链表也称为线性表的链式存储结构,其特征就是其中的元素不需要考虑相邻位置,每个元素都知道下一个元素的位置,这样,我们只需要知道第一个元素的地址就能遍历所有元素。

1.2 链表的结构

如图所示,常见的链表是这样表示的,一个元素会存放下个元素的地址,这样就不需要在空间上相邻排序,也能找到下个点。

1.3 链表的表示方法

如图,用Java可以这样表示一个链表,每个节点存放一个值val和下一个点的地址next。

当我们要指定一个点为头节点时,可以让head = next,比如这样:

ListNode head = next;

这样一来,我们就算确定了头节点,然后就可以进行插入和删除,修改查找等工作了。

我们以插入节点为例:

使用头插法,就是当我们插入数据时,先让该数据的下一个数据的地址指向head,更新 head 为 newNode,使新节点成为新的头节点,这样就完成了一个点的插入。接下来再插入的时候就同理,把新的点的next地址指向head,再把整个节点给head。这样一来每次插入点点都在head处,也就是最前面。

通过debug可以看到整个插入的过程。

当我们明白了链表的结构,除了实现头插法,我们还可以写出其他的方法实现,比如尾插法,任意位置插入,查找,删除等等。

1.4 其他链表

我们前面说的链表是有头的单向非循环链表,其实链表还分很多种情况,比如,单向双向,有头无头,循环非循环等等。

其实常用到的也就两种:

无头单向非循环链表:结构简单,一般不会单独用来存数据

无头双向链表:其在Java的集合框架库中LinkedList底层实现就是无头双向循环链表

这些我们可以自己实现也可以直接使用Java中的LinkedList。

2. LinkedList的结构

LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

其结构如下:

在集合框架中LinkedList实现了List接口。

3. LinkedList的使用

我们可以直接调用LinkedList类来直接使用其接口

方法 对应解释
boolean add(E e) 尾插e
void add(int index, E element) 尾插e到index位置
boolean addAll(Collection<? extends E> c) 尾插c中的元素
E remove(int index) 删除index位置元素
boolean remove(Object o) 删除遇到的第一个o
E get(int index) 获取index位置元素
E set(int index, E element) 将index位置元素设为elem
void clear() 清空
boolean contains(Object o) o是否在表中
int indexOf(Object o) 返回第一个o所在的下标
int lastIndexOf(Object o) 返回最后一个o所在的下标
List<E> subList(int fromIndex, int toIndex) 截取部分list

4. ArrayList 和 LinkedList的区别

在区别方面:

1. 遍历:ArrayList的遍历方式有三种:for循环下标,for each循环,迭代器;而LinkedList的遍历方式有两种,没有for循环下标,因为其存放地址的特性,无法获取下标。

2. 存储空间上:ArrayList物理上一定连续;而LinkedList逻辑上连续,但物理上不一定连续。

3. 随机访问:ArrayList 支持O(1);LinkedList 不支持:O(N)。

4. 头插:ArrayList需要搬移元素,效率低O(N);LinkedList只需修改引用的指向,时间复杂度为O(1)。

5. 插入:ArrayList空间不够时需要扩容;LinkedList没有容量的概念。

6. 应用场景:ArrayList元素高效存储+频繁访问;LinkedList任意位置插入和删除频繁。

5. 总结

通过ArrayList 和 LinkedList,我们知道了顺序表在物理结构上存在两种不同的存储方式,两种方式各有千秋。本质是要理解链表的底层结构,在空间上不需要相邻,只需要知道下一个元素的位置就能找到。