一、链表基础知识
链表是一种线性数据结构,与顺序表不同,它在内存中不是连续存储的,而是通过节点间的引用(指针)连接起来 。链表分为多种类型,常见的有无头结点、有头结点、单向、双向、循环等情况,其中无头结点、无环、单向链表是笔试面试中最常见的类型。
二、代码实现
1. 节点类定义
class Node {
// 节点存储的值
public String value;
// 节点的下一个元素
public Node next;
public Node(String value) {
this.value = value;
this.next = null;
}
}
2. 链表类定义
public class MyLinkedList {
// 头节点
private Node head = null;
}
三、链表操作实现
插入操作
1. 按位置插入节点
public void add(int index, String value) {
// 1. 先判断 index 是否合法
// index == size 的时候,要插入链表尾部,也是合法情况,不需要抛异常。
if (index < 0 || index > size()) {
throw new RuntimeException("下标超出范围");
}
// 2. 针对头插的特殊处理
if (index == 0) {
addFirst(value);
return;
}
// 3. 按照当前 value 值,创建新的节点。
Node newNode = new Node(value);
// 4. 找到 index 要插入位置的前一个节点
// 由于当前链表是“单向链表”每个节点,只能找到 next,
// 插入新节点,需要修改前一个节点的 next 值。
// 第一个节点的下标,默认就是 index - 1
Node prev = head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
// 5. 进行插入操作,把 prev 指向 index - 1 的位置。
newNode.next = prev.next;
prev.next = newNode;
}
2. 头插法
public void addFirst(String value) {
Node newNode = new Node(value);
newNode.next = head;
head = newNode;
}
删除操作
1. 按位置删除节点
public void remove(int index) {
if (index < 0 || index >= size()) {
throw new RuntimeException("下标超出范围");
}
if (index == 0) {
removeFirst();
return;
}
Node prev = head;
for (int i = 0; i < index - 1; i++) {
prev = prev.next;
}
Node toRemove = prev.next;
prev.next = toRemove.next;
}
2. 头删法
public void removeFirst() {
if (head == null) {
return;
}
head = head.next;
}
其他操作
1. 链表长度计算
public int size() {
int size = 0;
Node cur = head;
while (cur != null) {
size++;
cur = cur.next;
}
return size;
}
四、经典链表问题
1. 合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入: l1 = [1,2,4] , l2 = [1,3,4]
输出: [1,1,2,3,4,4]
解题思路:
- 创建新的链表表示最终结果。
- 搞两个引用,分别指向两个链表。
- 比较这两个引用的值,谁小,就把哪个节点取出来,插入到新链表的末尾。
- 如果这两个引用,有任何一个指向了 null ,说明该链表就结束了,就把另一个链表剩余的元素,都添加到新链表末尾即可。
2. 反转链表
示例:
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
解题思路:
使用迭代法,定义 prev (指向当前节点的前一个节点,初始为 null )和 cur (指向当前正在处理的节点,初始为头节点 head ) ,每次迭代中,先保存 cur.next (防止丢失后续链表),然后将 cur.next 指向 prev ,再将 prev 移动到 cur 位置, cur 移动到原来保存的 cur.next 位置,直到 cur 为 null ,此时 prev 就是反转后的头节点。 代码如下:
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode cur = head;
while (cur != null) {
ListNode nextTemp = cur.next;
cur.next = prev;
prev = cur;
cur = nextTemp;
}
return prev;
}
以上代码实现了链表的基本操作以及解决了部分经典链表问题,在实际应用中,链表常用于实现栈、队列等数据结构,以及在操作系统内存管理、文件系统等场景中有广泛应用。