原题链接: https://leetcode.com/problems/remove-nth-node-from-end-of-list/
1. 题目介绍
Given a linked list, remove the n-th node from the end of list and return its head.
给定一个链表,删除倒数第 n 个元素,然后返回新链表的头指针
Example:
Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
Note:
Given n will always be valid.
Follow up:
Could you do this in one pass?
注意:保证给定的 n 是有效的
可以在一遍循环中实现吗?
2. 解题思路
这个题非常适合作为链表类题目的入门题。要求做的事情,非常简单,只需要删除链表中倒数第 n 个元素即可。
我们来看一下节点中的属性和函数:
public class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; }
}
我们可以知道,这是一个单向链表,一个节点只能直到自己后面的节点是谁,而不知道前面的节点是谁。
注: 以下内容参考了 https://leetcode.com/problems/remove-nth-node-from-end-of-list/solution/
2.1 思路1:遍历两次
由于不能知道前面的节点是谁,我们可以有这样的思路:
- 求出整个链表中有多少个元素。比如有length个元素
- 然后对第length - n +1个元素进行移除,移除的方法就是让第 length-n 个节点的 next 直接指向 length -n +2 个节点。
总共遍历2次,一次是计算总长度length,一次是删除节点。
在遍历链表的过程中,有个技巧需要注意:
技巧:使用辅助节点
我们将添加一个无用的结点作为辅助节点,这个节点放在链表的最前面,算是第 0 个节点吧。辅助节点用来简化某些极端情况,例如链表中只含有一个结点,或需要删除的节点就是链表的头节点等等。
实现代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//辅助节点,用于简化链表节点只有1个、删除头节点 等情况
//放在整个链表的最前面
ListNode assist = new ListNode(0);
assist.next = head;
//求链表的长度:第一次遍历链表
int length = 0;
ListNode temp = head;
while(temp != null) {
length ++;
temp = temp.next;
}
int index = length - n;
//删除链表某个节点:第二次遍历
temp = assist;
while(index >0) {
index --;
temp = temp.next;
}
//此时temp会指向要删除的节点的前一个节点
//然后删除该节点即可
temp.next = temp.next.next;
return assist.next;
//这里不能return head,原因是head节点可能也是被删除的节点
}
}
2.2 思路2:一次遍历
上面的方法,为什么会遍历两次?因为一次求总长度,一次要删数组。
怎样才能做到只遍历一次呢?删除数组的那次遍历是省略不了的,那就只能省略求总长度的那次了。
于是问题转化为:如何在不知道总长度的情况下,删除链表的倒数第 n 个数。
下面有请 “双指针法” 隆重登场。
技巧:双指针法
定义一个左指针 left , 定义一个右指针 right 。左指针指向第 0 个元素,右指针指向第 n+1 个元素。让左指针和右指针之间,始终隔着 n 个元素,就像一个固定宽度的窗口一样。然后将这个窗口向右平移。
当右指针到达最后一个节点时,左指针就会指向链表的倒数第 n+1 个元素了。于是我们把倒数第n+1 个元素的 next 指针绕过 要删除的倒数第 n 个元素,指向倒数第 n-1个元素即可。
实现代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
//辅助节点,用于简化链表节点只有1个、删除头节点 等情况
//放在整个链表的最前面
ListNode assist = new ListNode(0);
assist.next = head;
ListNode left = assist;
ListNode right = assist;
//让right指向第n+1个元素,让左指针和右指针之间,始终保持 n 个元素的距离
for(int i = 1 ; i<=n+1 ; i++) {
right = right.next;
}
//平移窗口
while(right != null) {
right = right.next;
left = left.next;
}
left.next = left.next.next;
return assist.next;
//这里不能return head,原因是head节点可能也是被删除的节点
}
}
3. 参考资料
https://leetcode.com/problems/remove-nth-node-from-end-of-list/solution/