
本文详解如何在单链表中实现「跳过 m 个节点、随后删除 n 个节点」的循环操作,包含健壮性处理(空链表、边界越界、m=0/n=0 等)、面向对象设计修正(消除 static 误用),并提供可直接运行的 java 实现与关键逻辑说明。
本文详解如何在单链表中实现「跳过 m 个节点、随后删除 n 个节点」的循环操作,包含健壮性处理(空链表、边界越界、m=0/n=0 等)、面向对象设计修正(消除 static 误用),并提供可直接运行的 java 实现与关键逻辑说明。
在链表处理类问题中,「Skip-M-Delete-N」是一种典型区间遍历+结构修改任务:从头开始,每跳过 M 个有效节点后,立即删除紧随其后的 N 个节点,并重复该过程直至链表尾部。常见错误包括无限循环、空指针异常(NullPointerException)、静态/实例成员混用,以及缺乏边界防护。下面我们将以专业教程方式,逐步构建一个鲁棒、清晰、符合面向对象规范的解决方案。
✅ 核心设计原则
- 拒绝 static 链表状态:每个 LinkedList 实例应独立维护自己的 head 和 tail,避免多实例冲突;
- 方法属于实例:skipMdeleteN() 是对当前链表的操作,不应接收 head 参数,也不应为 static;
- 防御式编程:显式检查 head == null、m
- 逻辑分层清晰:跳过阶段(M 步)与删除阶段(N 步)解耦,避免嵌套循环导致的控制流混乱。
✅ 正确实现(含详细注释)
public class LinkedList {
public class Node {
int data;
Node next;
public Node(int data) {
this.data = data;
this.next = null;
}
}
public Node head; // 实例字段,非 static
public Node tail;
public void addFirst(int data) {
Node newNode = new Node(data);
newNode.next = head;
head = newNode;
if (tail == null) tail = head; // 维护 tail(可选,本例未使用)
}
public void print() {
Node temp = head;
while (temp != null) {
System.out.print(temp.data + "->");
temp = temp.next;
}
System.out.println("null");
}
/**
* 跳过 m 个节点,然后删除接下来的 n 个节点,循环执行直到链表末尾
* @param m 要跳过的节点数(>= 0)
* @param n 要删除的节点数(>= 0)
*/
public void skipMdeleteN(int m, int n) {
// 边界防护:无节点或无需删除 → 直接返回
if (n <= 0 || head == null) return;
// 特殊情况:m == 0 → 全部删除(保留空链表)
if (m == 0) {
head = null;
return;
}
Node curr = head;
while (curr != null) {
// 【Step 1】跳过 m-1 个节点(到达第 m 个节点)→ curr 指向第 m 个节点
for (int i = 1; i < m; i++) {
curr = curr.next;
if (curr == null) return; // 提前结束:不足 m 个可跳
}
// 【Step 2】准备删除:ptr 指向第 m 个节点(即待保留段的末尾)
Node ptr = curr;
// 【Step 3】向前推进 n+1 步:使 curr 指向「删除段之后的第一个节点」
// 例如:1→2→3→4→5→6,m=2,n=2 → 在 2 处停止,删除 3,4 → curr 应停在 5
for (int i = 0; i <= n; i++) {
curr = curr.next;
if (curr == null) break; // 防止越界
}
// 【Step 4】断开连接:ptr.next 指向 curr(跳过中间 n 个节点)
ptr.next = curr;
}
}
public static void main(String[] args) {
LinkedList ll = new LinkedList();
// 构建链表:1→2→3→4→5→6→7→8→null
ll.addFirst(8);
ll.addFirst(7);
ll.addFirst(6);
ll.addFirst(5);
ll.addFirst(4);
ll.addFirst(3);
ll.addFirst(2);
ll.addFirst(1);
System.out.print("Original: ");
ll.print(); // 输出:1->2->3->4->5->6->7->8->null
ll.skipMdeleteN(2, 2); // 跳 2(1,2),删 2(3,4);跳 2(5,6),删 2(7,8)
System.out.print("After M=2,N=2: ");
ll.print(); // 输出:1->2->5->6->null
}
}⚠️ 关键注意事项
- for (int i = 1; i :因为初始 curr 已指向第 1 个节点,只需再走 m−1 步到达第 m 个节点;
- 删除段推进 n+1 步:ptr 是保留段尾节点,要跳过 n 个待删节点,需让 curr 停在第 n+1 个后续节点上,才能通过 ptr.next = curr 完成“切除”;
- curr == null 的双重检查:既在跳过循环中提前退出,也在推进删除段时 break,避免 curr.next 触发 NPE;
- m == 0 的语义是“不跳,直接删全部”:此时整个链表被清空,head = null 是唯一正确行为;
- n == 0 或 n :避免无效操作或逻辑错误。
✅ 运行验证
输入链表:1→2→3→4→5→6→7→8,M=2, N=2
执行过程:
- 起点 curr = 1 → 跳 2 步至 2(curr 指向 2)→ 删除后续 2 个(3,4)→ 2.next = 5
- curr = 5 → 跳 2 步至 6 → 删除后续 2 个(7,8)→ 6.next = null
最终链表:1→2→5→6→null,完全符合预期。
该实现兼顾正确性、可读性与工程健壮性,适用于面试手写、LeetCode 类题目及实际项目中的链表区间管理场景。










