Medium
题目描述
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
提示:
- 链表中结点的数目为
sz 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
**进阶:**你能尝试使用一趟扫描实现吗?
解题思路
解题思路
这道题要求删除链表倒数第 n 个结点,有两种常见的解法:
方法一:两次遍历
先遍历一次链表统计总长度,然后计算出要删除的结点在正数第几位,再进行删除。虽然思路简单,但需要遍历两次。
方法二:双指针(推荐)
使用快慢指针的技巧,让快指针先走 n+1 步,然后快慢指针同时移动。当快指针到达末尾时,慢指针恰好指向要删除结点的前一个位置。
具体步骤:
- 创建一个哨兵结点指向 head,这样可以统一处理删除头结点的情况
- 设置 fast 和 slow 两个指针都指向哨兵结点
- fast 指针先移动 n+1 步
- 然后 fast 和 slow 同时移动,直到 fast 指向 null
- 此时 slow 指向要删除结点的前一个结点,执行删除操作
使用哨兵结点的好处是避免了特殊处理删除头结点的情况,代码更加简洁统一。
代码实现
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
ListNode* fast = dummy;
ListNode* slow = dummy;
// fast指针先移动n+1步
for (int i = 0; i <= n; i++) {
fast = fast->next;
}
// fast和slow同时移动
while (fast != nullptr) {
fast = fast->next;
slow = slow->next;
}
// 删除倒数第n个结点
ListNode* nodeToDelete = slow->next;
slow->next = slow->next->next;
delete nodeToDelete;
ListNode* result = dummy->next;
delete dummy;
return result;
}
};
class Solution:
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
dummy = ListNode(0, head)
fast = slow = dummy
# fast指针先移动n+1步
for _ in range(n + 1):
fast = fast.next
# fast和slow同时移动
while fast:
fast = fast.next
slow = slow.next
# 删除倒数第n个结点
slow.next = slow.next.next
return dummy.next
public class Solution {
public ListNode RemoveNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
ListNode fast = dummy;
ListNode slow = dummy;
// fast指针先移动n+1步
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
// fast和slow同时移动
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
// 删除倒数第n个结点
slow.next = slow.next.next;
return dummy.next;
}
}
var removeNthFromEnd = function(head, n) {
const dummy = new ListNode(0, head);
let fast = dummy;
let slow = dummy;
// fast指针先移动n+1步
for (let i = 0; i <= n; i++) {
fast = fast.next;
}
// fast和slow同时移动
while (fast !== null) {
fast = fast.next;
slow = slow.next;
}
// 删除倒数第n个结点
slow.next = slow.next.next;
return dummy.next;
};
复杂度分析
| 复杂度 | 双指针解法 |
|---|---|
| 时间复杂度 | O(L),其中 L 是链表长度,只需要一次遍历 |
| 空间复杂度 | O(1),只使用常数个额外变量 |