1、题目描述:
给定一个链表,删除链表的倒数第n个节点,并且返回链表的头节点
示例:
给定一个链表:1->2->3->4->5,和 n = 2
当删除了倒数第二个节点后,链表变为 1->2->3->5
说明:给定的n保证是有效的,尝试使用一趟扫描实现。
2、分析如下:
- 首先想当我们要删除单链表的某个节点的时候,有两种办法:
A:一个让当前节点的下一个节点的数据域不 断覆盖当前节点的数据域名,然后释放尾节点就好
B:直接让当前节点的前驱的next域指向当前节点的next域这个是最快捷有效的办法。 - 删除搞定了,此时我们需要考虑一下倒数第K个节点。
A :要找到倒数第K个节点一定是要遍历链表的,这里定义slow来标识这个K
B:这里假设找的是倒数第3个节点,倒数第三个节点是657,这里找的是倒数,可以定义一个变量fast指向尾节点888,从尾节点 倒着走两步就是倒数第三个节点,要找的。
C:看到这里slow和fast差2步,三个节点能产生的是两个间隔,那就是要找的倒数第K个节点,从尾节点fast走slow就永远差k-1步,这是一个固定的间距。
D:既然这一步能看懂,我们不妨接着大胆的设想,让slow和fast就保持这个间距一起遍历链表,这样当fast走到尾节点的时候,我们的slow就指向的是倒数第K个节点。
F:前面我们知道要找倒数第k个,那fast和slow相差的就是K-1步,三个节点,两个间隔,就是2步嘛,所以我们可以先让fast走k-1步,然后fast和slow一起走,这样当fast走到头,slow就是要删除的喽。
- 既然要删除的节点已经找到了,这样我们再定义一个prev始终指向slow的前驱就好,然后让prev.next = slow.next即可。
public ListNode removeNthFromEnd(ListNode head, int k) {
ListNode cur = head;
int count = 0;
while(cur != null){
count++;
cur = cur.next;
}
//当倒数n个节点和链表长度相等时,需要特殊考虑。
if(count == k){
return head.next;
}
ListNode fast = head;
ListNode slow = head;
//fast多走的步数
while(k-1 > 0){
if(fast == null){
return null;
}
fast = fast.next;
k--;
}
//两个一起走找到K节点并且删除
ListNode prev = null;
while(fast != null && fast.next != null){
prev = slow;
fast = fast.next;
slow = slow.next;
}
prev.next = slow.next;
return head;
}
注意:
- 当节点长度count = n的时候,这里包含了一个节点的情况或者多个节点。很容易出现单纯的认为只有一个节点就直接返回null,出现了误区,还有即便是只有一个节点,这样的理解也是不够合理的,因为对于带头节点的链表来讲,当删除head,他的头就是head.next。