一.原题如下
二.解题思路
- 这道题的解决方法是使用快慢指针(即相对而行),同时指向虚拟头节点
- 需要注意的是,因为删除的是单链表的倒数节点,所以需要找到倒数节点的前驱节点,才能进行删除;
- 如果删除的倒数节点正好是第一个节点,那么就会出现前驱节点空指针报错,所以需要定义一个虚拟头节点,用来指向整个链表
- 快指针先走n步,然后快慢指针再一起走,直到快指针走到尽头,那么此刻慢指针指向的即为倒数n+1个节点(倒数节点的前驱节点)
- 最后进行节点删除,然后返回虚拟头节点的后继节点即可
三.复杂度
- 时间复杂度:O(n)
- 空间复杂度:O(1)
四.完整题解
具体步骤请查看每个关键步骤的代码注释
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 定义虚头节点,避免倒数删除到头节点时的空指针
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 定义快慢指针,同时指向dummy
ListNode slow = dummy, fast = dummy;
// 快指针先走n+1步,为什么是n+1,因为单链表要删除当前节点必须找到其上一个节点
for (int i = 0; i < n + 1; i++) {
fast = fast.next;
}
// 快慢指针同时走,直到快指针走到尽头,慢指针所指向的即是倒数节点
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
// 删除节点
slow.next = slow.next.next;
// 返回虚拟头节点的后继节点,即为删除后的新链表
return dummy.next;
}
}