LeetCode19 删除链表倒数第n个节点
题目描述
给定一个链表,删除链表中的倒数第n个节点,并且返回链表的头节点
说明:给定的n保证是有效的
提示:双指针
注意:是倒数第n个节点
进阶:使用一趟遍历扫描实现
示例
给定一个链表:1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后(值为4),链表变为 1->2->3->5.
解题思路
首先既然是链表,那么自然而然的想到一个删除的逻辑就是找到要删除节点的前一个值(记为pre),然后使用下面语句删除。
pre.next = pre.next.next;
一开始拿到这个题目的时候,想着既然要删除的是倒数的节点,那么从后往前面找第n个就是了,但是链表又不是数组,能够直接得到其长度,也没有索引这个说法。顺着这个思路,发现可以这样:
- 第一趟遍历找出链表的长度len
- 第二趟遍历,遍历len-n次,此时遍历到的节点为要删除节点的前一个
(上面方法并没有实现,不过应该是可行的)
但是发现这样做两次遍历,而且存在重复无用的操作,并且题目都提示用一次遍历和双指针的方式了。就接着想究竟怎么样才能体现双指针呢?所谓双指针基本上都是一快一慢,一个指向后面的元素,一个指向前面的元素。
同时,这个倒数第n这个数字我们也还没有用上。即然题目给的是链表,我们只能让它遍历到空的时候才能知道倒数这个边界究竟在哪。那么此时如果我们让一个指针指向该空,另外一个指针与该指针相差n+1个元素,那么该指针指向的就是要删除元素的前置节点了。使用这种方法即可实现一趟遍历,我们只要遍历到空的时候就能得出结果。
可以想象成一个格子,该格子每次框住n+1个元素,该格子的左右边界即为左右指针的指向元素。
注意:
由于我们要找到的是要删除节点的前置节点,但是如果要删除的节点为第一个时前置节点为空,这个时候就会出现问题了。因此类似这种通常的做法是在第一个节点之前在添加一个节点,其下一个节点指向第一个节点,数据域不用管,即头节点,也有大佬称之为哨兵节点。
程序实现
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode first = new ListNode(-1); //头结点, -1可以随便赋值
first.next = head; //指向第一个节点
ListNode right = first; //“格子”的右边界
ListNode left = first; //“格子”的左边界
for (int k = 0; k < n + 1; k++) { //当left指向第0个节点时(头节点),right应该指向第n+1个节点
right = right.next;
}
while (right != null) { //直到“格子”右边界为空
left= left.next;
right = right.next;
}
left.next = left.next.next; //删除节点
return first.next; //返回第一个节点
}
}