
题目描述
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明: 给定的 n 保证是有效的。
进阶: 你能尝试使用一趟扫描实现吗?
❝来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
❞
解题思路
删除链表中指定节点的通常做法是:遍历到相应的位置,并修改前一个节点的 next 指针指向被删除节点的下一个节点,如下图所示。

本题要求删除倒数第 n 个节点,因此需要知道链表的长度 l,用 l-n 即为节点所在的位置。
常规的做法是两次遍历:
第一遍先遍历链表长度
第二遍遍历到指定的节点,然后删除
但本题目要求使用一次遍历,因此需要使用一些技巧。
「双指针」单次遍历的思路:
利用两个指针 p1,p2,从左到右依次遍历,p1 在前,p2 在后。
p1 先向后移动 n 分节点,两个指针之间的差距是 n。
当 p1 指针遍历到结尾时,p2 正好指向被删除的节点的位置。
进而可以删除链表的倒数第 n 个节点。

解题代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
//构造一个虚拟节点,指向链表头节点
ListNode* h1 = new ListNode(0);
h1->next = head;
ListNode* p1 = h1;//p1->next指向链表的头节点
ListNode* p2 = h1;//p2->next指向链表的头节点
//p1指针先走n个节点
for (int i = 0; i < n ; i++ )
p1 = p1->next;
//p1,p2向后同时移动,直到指向最后一个节点
while (p1->next != NULL)
{
p1 = p1->next;
p2 = p2->next;
}
//此时p2指向的是被删除节点的前一个节点
p2->next = p2->next->next;
//返回链表的头节点
return h1->next;
}
};
时间复杂度: O(L) , L 为链表长度 空间复杂度: O(1)
分析总结创建一个虚拟节点,指向头结点,这样做可以回避掉如果链表只有一个结点,需要单独处理的情况。
删除一个结点时,需要定位到这个待删除结点的前驱结点,所以要使 p2 指向待删除结点的前驱,而不是指向待删除结点本身。
❝本文转载软件工程实践领航营
❞