链表的探索研究
链表的定义
链表是一种 线性数据结构,和数组一样存储一组元素。
区别:
数组:内存连续,随机访问 O(1),但插入/删除成本高 O(n)。
链表:内存不连续,依靠指针连接,插入/删除 O(1),但访问某个位置 O(n)。
链表的分类
1. 按指针方向分类
单向链表(Singly Linked List)
每个节点只有一个指针 next,指向下一个节点。只能从头到尾遍历。
优点:结构简单,内存消耗少。
缺点:不能反向遍历,删除/插入需要找到前驱节点。
class Node {int val;Node next;
}
//单向链表(Singly Linked List)
//每个节点只保存 next 指针,指向下一个节点。
//无法从后往前遍历。class ListNode {int val;ListNode next;ListNode(int val) {this.val = val;}
}
双向链表(Doubly Linked List)
每个节点有两个指针:prev 和 next,分别指向前后节点。可以双向遍历。插入、删除更灵活,但多占用一个指针空间。
class DNode {int val;DNode prev, next;
}
//双向链表(Doubly Linked List)
//节点有两个指针:prev 和 next,可双向遍历。
//插入/删除更灵活,但需要更多内存。
class DNode {int val;DNode prev, next;DNode(int val) {this.val = val;}
}
2. 按首尾是否相连分类
非循环链表:
- 尾节点的 next 指针为 null。
- 最常见的链表形式。
循环链表(Circular Linked List):
- 尾节点的 next 指向头节点,形成环。
- 可以从任意节点出发遍历整个链表。
- 可用于 约瑟夫环、循环队列 等问题。
- 尾节点指向头节点
tail.next = head;
按节点存储数据数量分类
单元素链表节点
- 每个节点只存储一个数据。
多元素链表节点(不常用)
- 每个节点可存储多个数据元素,类似“块状链表”,在某些场景(比如内存优化)会用到。
静态链表
- 不使用指针,而是用数组下标表示 next。
- 常用于不能动态分配内存的语言(如 C 语言中的结构体 + 数组)。
跳表(Skip List)
- 本质是多层链表,每一层是下层的“索引”。
- 通过跳跃访问加快查找速度,查找效率接近二分查找。
LinkedList
Java 标准库中的 LinkedList
Java 提供了现成的 java.util.LinkedList,底层是 双向链表。
特点:
- 实现了 List、Deque 接口。
- 插入、删除效率高。
- 不支持随机访问(不像 ArrayList 的 O(1))。
常用方法:
LinkedList<Integer> list = new LinkedList<>();
list.add(10); // 尾插
list.addFirst(5); // 头插
list.addLast(20); // 尾插
list.removeFirst(); // 删除头
list.removeLast(); // 删除尾
list.get(0); // 访问第一个元素 (O(n))
- 链表的基本操作(手写实现)
① 头插法
public void addFirst(int val) {ListNode node = new ListNode(val);node.next = head;head = node;
}
② 尾插法
public void addLast(int val) {ListNode node = new ListNode(val);if (head == null) {head = node;return;}ListNode cur = head;while (cur.next != null) {cur = cur.next;}cur.next = node;
}
③ 删除节点
public void remove(int val) {if (head == null) return;if (head.val == val) {head = head.next;return;}ListNode cur = head;while (cur.next != null) {if (cur.next.val == val) {cur.next = cur.next.next;return;}cur = cur.next;}
}
④ 查找节点
public boolean contains(int val) {ListNode cur = head;while (cur != null) {if (cur.val == val) return true;cur = cur.next;}return false;
}
- 链表常见题型(面试高频)
反转链表public ListNode reverseList(ListNode head) {ListNode prev = null;ListNode cur = head;while (cur != null) {ListNode next = cur.next;cur.next = prev;prev = cur;cur = next;}return prev;
}
寻找链表中间节点(快慢指针)
public ListNode middleNode(ListNode head) {ListNode slow = head, fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;}return slow;
}
检测环形链表(Floyd 判圈法)
public boolean hasCycle(ListNode head) {ListNode slow = head, fast = head;while (fast != null && fast.next != null) {slow = slow.next;fast = fast.next.next;if (slow == fast) return true;}return false;
}
合并两个有序链表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(-1);ListNode cur = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {cur.next = l1;l1 = l1.next;} else {cur.next = l2;l2 = l2.next;}cur = cur.next;}cur.next = (l1 != null) ? l1 : l2;return dummy.next;
}