- 使用双指针 和 虚拟头结点dummy
- 首先定义slow 和 fast 指向虚拟头结点dummy
- 执行while(n-- > 0)时,将fast向后移动,即会将fast向后移动n步。此处删除倒数第3个结点,即将fast向后移动3步。
- 定义一个pre结点
- 当fast非空的时候,先将pre 指向 slow,再将slow和fast都向后移动,这样的效果就是让pre指向slow的前面一个位置
- 当fast指向null的时候,执行:pre.next = slow.next; 让pre指向slow的后面一个结点, 即绕过待删除节点slow 直接指向slow的下一节点(slow.next)
- 二刷:此处不能定义两个节点都是头结点,会报错,但是可以定义两个节点都是虚拟头结点
- 对链表进行插入删除 和 对链表进行查询是两种不同的操作,前者需要设置虚拟头结点,后者则不需要,因此前者定义slow和fast的时候,将其赋值为了dummy
- 不需要在while(true) 循环内操作,直接按顺序来操作即可
方式一
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode slow = dummy;
ListNode fast = dummy;
while(n-- > 0){
fast = fast.next;
}
ListNode pre = null;
while(fast != null){
pre = slow;
slow = slow.next;
fast = fast.next;
}
pre.next = slow.next;
slow.next = null;// 释放 待删除节点slow 的next指针, 这句删掉也能AC
return dummy.next;
}
}
方式二
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode node = findFromEnd(dummy, n + 1);
node.next = node.next.next;
return dummy.next;
}
public ListNode findFromEnd(ListNode head, int m){
ListNode fast = head;
ListNode slow = head;
while(m-- > 0){
fast = fast.next;
}
while(fast != null){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}