算法思想:
使用了双指针法。下面是详细的算法思想:
1. 引入虚拟头节点(dummy node)
- 为了处理链表的一些边界情况(比如删除头节点),我们在链表的头部引入了一个虚拟节点
dummy,并让它指向原来的头节点head。这样,无论我们要删除哪个节点,处理过程都变得更加统一和简单。
2. 定义两个指针:快指针(fast)和慢指针(slow)
- 我们使用两个指针,
fast和slow,最初都指向虚拟头节点dummy。 - 快指针
fast会比慢指针slow超前移动n+1步。这样,当fast指向链表末尾(null)时,slow刚好指向要删除节点的前一个节点。
3. 移动快指针
- 首先,快指针
fast先向前移动n+1步,这样可以确保快指针和慢指针之间相隔n个节点。
4. 同时移动快慢指针
- 接下来,快慢指针一起向前移动,直到快指针到达链表的末尾。这时,慢指针
slow就刚好处于要删除节点的前一个位置。
5. 删除节点
- 现在,慢指针
slow的下一个节点就是我们需要删除的节点。通过slow.next = slow.next.next,我们跳过了这个节点,达到了删除的目的。
6. 返回新的头节点
- 最后,返回
dummy.next。注意,链表的头节点可能发生了变化(如果原来的头节点被删除),因此我们返回虚拟节点dummy的下一个节点作为新的链表头节点。
代码核心思路总结:
- 通过快慢指针法,仅需遍历链表一次(一次循环)就可以找到倒数第N个节点,并将其删除,时间复杂度为 O(L),其中 L 是链表的长度。空间复杂度为 O(1),因为只用了常数级别的额外空间。
示例分析:
假设输入链表为 [1, 2, 3, 4, 5],n = 2,即删除倒数第二个节点。
- 初始化:
fast和slow都指向虚拟节点dummy。 - 快指针前移:
fast先向前移动n+1 = 3步,指向节点3。 - 同步移动:同时移动
fast和slow,直到fast指向null,此时slow指向节点3的前一个节点,即节点2。 - 删除节点:通过
slow.next = slow.next.next删除节点4,最终链表变为[1, 2, 3, 5]。
这样就成功地删除了倒数第2个节点。

java 实现代码:
/*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val = val; }* ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode removeNthFromEnd(ListNode head, int n) {ListNode dummyNode = new ListNode(0);dummyNode.next = head;ListNode slow = dummyNode; //dummyNode,slow,fast都是引用类型ListNode fast = dummyNode;for(int i = 0; i <= n; i++) { //快指针先移动 n+1 步fast = fast.next;}while(fast != null) { //然后快慢指针一起移动slow = slow.next;fast = fast.next;}slow.next = slow.next.next;return dummyNode.next;}
}