目录
1. 简介
链表是一种常用的数据结构,其特点是数据的线性排列和动态的存储分配。
2. 图解
链表可分为单向链表和双向链表:
2.1 单向链表
每个节点包含一个指向下一个节点的指针,类似美国男篮队员之间的传递,一代接着一代。
public class Node {
public int value;
public Node next;
public Node(int v){
value = v;
}
}
2.2 双向链表
每个节点不仅有指向下一个节点的指针,还有指向前一个节点的指针,像中国男篮队员之间既有向前的传球也有向后的回传。
public class DoubleNode {
public int value;
public DoubleNode pre;
public DoubleNode next;
public DoubleNode (int v){
value = v;
}
}
3. 应用
每说到链表,就会与数组进行对比。
在Java中,LinkedList(链表) 和 ArrayList(数组),都是十分常用的数据容器。
相较于ArrayList,
- 链表更适用于需要频繁的在列表开头、中间、末尾等位置进行添加和删除元素操作。
- 如果需要频繁地访问列表元素还是ArrayList好用,毕竟LinkedList需要通过循环迭代来访问列表中的某些元素。
3.1 链表插入
插入和删除时链表的一大亮点。
相较于数组,数组最大的缺点就是,当我们在插入和删除时需要移动大量的元素。
链表的插入操作通常涉及以下几个步骤:
- 定位插入点:确定在链表的哪个位置插入新节点。这可能需要遍历链表直到找到合适的位置。
- 创建新节点:分配一个新的节点,并将要插入的数据赋给这个新节点的数据部分。
- 修改指针:如果插入点是链表的开始,新节点的指针将指向原链表的第一个节点,同时链表的头指针需要更新为指向新节点。如果插入点是链表中间或末尾,需要修改前后两个节点的指针,使新节点被正确链接到链表中。
- 释放内存(可选):如果新节点是动态分配的内存,当不再需要这个节点时,应确保释放其占用的内存。
注:在一些编程语言中,如Java和C#等,内存管理是自动进行的,程序员不需要手动释放内存。但在C和C++等语言中,需要程序员显式地释放动态分配的内存。
3.1.1 在已知节点后插入
public class Node {
public int value;
public Node next;
public Node(int v){
value = v;
}
}
public class MyLinkedList {
Node head; // 链表的头节点
public void insertAfter(Node preNode, int newData) {
if (preNode == null) {
return;
}
// 创建新节点
Node newNode = new Node(newData);
// 将新节点插入
newNode.next = preNode.next;
preNode.next = newNode;
}
}
3.1.2 在尾部插入
public class Node {
public int value;
public Node next;
public Node(int v){
value = v;
}
}
public class LinkedList {
// 链表的头节点
Node head;
// 在链表末尾添加一个新节点
public void add(int data) {
// 创建新节点
Node newNode = new Node();
newNode.value = data;
newNode.next = null; // 新节点是最后一个节点,所以next为null
if (head == null) {
// 如果链表为空,新节点就是头节点
head = newNode;
} else {
// 找到链表的最后一个节点
Node last = head;
while (last.next != null) {
last = last.next;
}
last.next = newNode; // 将新节点链接到最后一个节点
}
}
}
3.2 LinkedList细节
LinkedList 继承了 AbstractSequentialList 类,支持迭代器Iterator。
由于LinkedList不支持 RandomAccess,通过 迭代器遍历或增强for 会比 普通的for循环+get方法效率要高。所以遍历链表时,建议使用迭代器或者增强型的for循环。
LinkedList 实现了 Deque 接口,可作为队列使用。
LinkedList 实现了 List 接口,可进行列表的相关操作。
LinkedList 实现了 Cloneable 接口,可实现克隆。
LinkedList 实现了Serializable 接口,可支持序列化,能通过序列化去传输。
4. 总结
链表是一种由一系列节点组成,每个节点包含数据和指向下一个节点的指针的数据结构。
- 优点:与数组相比,链表的优势在于能够动态地添加和删除元素,不需要预先分配固定大小的空间。
- 缺点:链表的内存开销比数组大,因为它需要额外的空间来存储指针。此外,链表的随机访问性能较差,因为必须从头部开始逐个遍历节点才能访问到目标元素,以及占用更多的内存空间来存储指针信息。
- 实际应用:它们在处理需要频繁插入删除操作的场景下特别有用。
- 实现细节:链表的增删改查操作通常需要遍历链表来完成,而在有虚拟头节点的情况下,可以简化一些操作的实现。虚拟头节点是一个不存储数据的节点,它的作用是为了统一操作逻辑,特别是在处理头节点和尾节点时。
学习链表不仅是为了掌握这一数据结构本身,还有助于加深对数据结构和算法设计原则的理解,提高解决实际问题的能力。