本系列可作为JAVA学习系列的笔记,文中提到的一些练习的代码,小编会将代码复制下来,大家复制下来就可以练习了,方便大家学习。
点赞关注不迷路!您的点赞、关注和收藏是对小编最大的支持和鼓励!
系列文章目录
拓展目录
手把手教你用 ArrayList 实现杨辉三角:从逻辑推导到每行代码详解
目录
目录
[1. 删除链表中等于给定值 val 的所有节点](#1. 删除链表中等于给定值 val 的所有节点)
[2. 反转一个单链表](#2. 反转一个单链表)
[3. 返回链表的中间结点(偶数时返回第二个)](#3. 返回链表的中间结点(偶数时返回第二个))
[4. 输出链表中倒数第 k 个结点](#4. 输出链表中倒数第 k 个结点)
[5. 合并两个有序链表](#5. 合并两个有序链表)
[6. 以给定值 x 为基准分割链表](#6. 以给定值 x 为基准分割链表)
前言
小编作为新晋码农一枚,会定期整理一些写的比较好的代码,作为自己的学习笔记,会试着做一下批注和补充,如转载或者参考他人文献会标明出处,非商用,如有侵权会删改!欢迎大家斧正和讨论!
链表是数据结构中的基础且高频考点,本文通过 6 道经典题目,带你系统掌握链表的核心操作技巧,包括删除、反转、快慢指针、合并与分割。每道题都配有完整思路拆解、代码实现和复杂度分析,适合刚学链表的同学巩固基础,也适合面试前快速复习。
1. 删除链表中等于给定值 val 的所有节点
题目描述 :删除链表中所有值为 val 的节点,返回处理后的头节点。
思路拆解
- 痛点分析:如果直接操作原链表,当头节点就是要删除的节点时,需要单独处理,逻辑会变得复杂。
- 核心技巧:使用 ** 哨兵节点(哑节点)** 作为虚拟头节点,统一处理所有节点的删除逻辑,无需单独判断头节点。
- 步骤
- 创建一个哨兵节点,让它的
next指向原链表的头节点。 - 用一个
prev指针从哨兵节点开始遍历。 - 如果
prev.next的值等于val,就跳过这个节点(prev.next = prev.next.next);否则,prev指针后移。 - 最终返回哨兵节点的
next,也就是新链表的头节点。
- 创建一个哨兵节点,让它的
完整代码
java
public ListNode removeElements(ListNode head, int val) {
// 创建哨兵节点
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prev = dummy;
while (prev.next != null) {
if (prev.next.val == val) {
// 删除目标节点
prev.next = prev.next.next;
} else {
prev = prev.next;
}
}
// 返回新链表的头节点
return dummy.next;
}
复杂度分析
- 时间复杂度:O (n),需要遍历一次链表。
- 空间复杂度:O (1),仅使用常量额外空间。
2. 反转一个单链表
题目描述:反转链表,返回新的头节点。
思路拆解
- 核心技巧 :迭代法 ,用三个指针(
prev、cur、next)逐步改变节点的指向。 - 步骤
- 初始化
prev为null,cur指向原链表头节点。 - 遍历链表,每次保存当前节点的下一个节点到
next。 - 将当前节点的
next指向prev,完成一次局部反转。 - 移动
prev和cur指针,继续处理下一个节点。 - 遍历结束后,
prev就是新链表的头节点。
- 初始化
完整代码
java
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;
}
复杂度分析
- 时间复杂度:O (n),遍历一次链表。
- 空间复杂度:O (1),仅使用常量额外空间。
3. 返回链表的中间结点(偶数时返回第二个)
题目描述:返回链表的中间节点,如果有两个中间节点,则返回第二个。
思路拆解
- 核心技巧 :快慢指针法。快指针每次走两步,慢指针每次走一步,当快指针走到链表末尾时,慢指针恰好指向中间节点。
- 步骤
- 初始化快慢指针都指向头节点。
- 循环条件:
fast != null && fast.next != null(保证快指针能走两步,避免空指针异常)。 - 快指针每次走两步,慢指针每次走一步。
- 循环结束后,慢指针就是中间节点。
完整代码运行
java
public ListNode middleNode(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
复杂度分析
- 时间复杂度:O (n),遍历一次链表。
- 空间复杂度:O (1),仅使用常量额外空间。
4. 输出链表中倒数第 k 个结点
题目描述:输入一个链表,输出该链表中倒数第 k 个节点。
思路拆解
- 核心技巧 :快慢指针法 。让快指针先走
k步,然后快慢指针一起走,当快指针走到链表末尾时,慢指针恰好指向倒数第k个节点。 - 步骤
- 初始化快慢指针都指向头节点。
- 快指针先走
k步。 - 快慢指针一起走,直到快指针走到链表末尾。
- 此时慢指针指向的就是倒数第
k个节点。
完整代码
java
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
// 快指针先走k步
for (int i = 0; i < k; i++) {
fast = fast.next;
}
// 快慢指针一起走
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
复杂度分析
- 时间复杂度:O (n),遍历一次链表。
- 空间复杂度:O (1),仅使用常量额外空间。
5. 合并两个有序链表
题目描述:将两个升序链表合并为一个新的升序链表并返回。
思路拆解
- 核心技巧:使用 ** 哨兵节点(哑节点)** 作为结果链表的起点,简化头节点的处理。
- 步骤
- 创建一个哨兵节点,用
cur指针指向它,作为结果链表的尾部指针。 - 遍历两个链表,每次选择值较小的节点接到
cur的后面。 - 移动对应链表的指针和结果链表的尾部指针。
- 当其中一个链表遍历完,将另一个链表的剩余部分直接接到结果链表的尾部。
- 返回哨兵节点的
next,也就是合并后链表的头节点。
- 创建一个哨兵节点,用
完整代码
java
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
while (list1 != null && list2 != null) {
if (list1.val < list2.val) {
cur.next = list1;
list1 = list1.next;
} else {
cur.next = list2;
list2 = list2.next;
}
cur = cur.next;
}
// 处理剩余节点
cur.next = list1 != null ? list1 : list2;
return dummy.next;
}
复杂度分析
- 时间复杂度:O (n + m),n 和 m 分别为两个链表的长度。
- 空间复杂度:O (1),仅使用常量额外空间。
6. 以给定值 x 为基准分割链表
题目描述:将链表分割为两部分,所有小于 x 的节点排在大于或等于 x 的节点之前。
思路拆解
- 核心技巧:把链表拆分成两个子链表,再进行拼接。
- 步骤
- 定义四个指针:
lessHead/lessTail存储小于x的节点,moreHead/moreTail存储大于等于x的节点。 - 遍历原链表,将每个节点根据值的大小加入对应的子链表。
- 遍历每个节点时,先保存它的下一个节点,然后断开它与原链表的连接(避免成环)。
- 拼接两个子链表:如果小于
x的链表为空,直接返回大于等于x的链表;否则,将大于等于x的链表拼接到小于x的链表的尾部。 - 返回拼接后链表的头节点。
- 定义四个指针:
完整代码
java
public ListNode partition(ListNode pHead, int x) {
ListNode lessHead = null, lessTail = null;
ListNode moreHead = null, moreTail = null;
ListNode cur = pHead;
while (cur != null) {
ListNode next = cur.next;
cur.next = null; // 断开原连接,避免成环
if (cur.val < x) {
if (lessHead == null) {
lessHead = lessTail = cur;
} else {
lessTail.next = cur;
lessTail = lessTail.next;
}
} else {
if (moreHead == null) {
moreHead = moreTail = cur;
} else {
moreTail.next = cur;
moreTail = moreTail.next;
}
}
cur = next;
}
// 拼接两个链表
if (lessHead == null) {
return moreHead;
}
lessTail.next = moreHead;
return lessHead;
}
复杂度分析
- 时间复杂度:O (n),遍历一次链表。
- 空间复杂度:O (1),仅使用常量额外空间。

总结
这 6 道题覆盖了链表的核心操作技巧,包括:
- 哨兵节点:用于统一处理头节点的边界情况(如删除、合并)。
- 快慢指针:用于找中间节点、倒数第 k 个节点等问题。
- 迭代反转:通过指针改变节点指向实现链表反转。
- 拆分 + 拼接:用于链表分割问题。
掌握这些技巧后,你可以应对绝大多数链表相关的面试题。
总结
以上就是今天要讲的内容,本文简单记录了java数据结构,仅作为一份简单的笔记使用,大家根据注释理解,您的点赞关注收藏就是对小编最大的鼓励!