1.题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
输入:head = [1], n = 1
输出:[]
输入:head = [1,2], n = 1
输出:[1]
2.想法
- 首先想到获取到这个链表的长度,再用长度减去第n个数就好了
但是这个只让遍历一次,在以前学习的数据结构书中,我就遇到过这个题,当时这道题还只是一个疑问题,问能不能之遍历一次完成删除倒数第n个节点,第一次回答的是不能,后来又改成,如果知道长度就可以,现在想想当时遇到这个问题,还是缺乏思考。
- 我看了下提示,说是使用双指针,这样自己还是少了点头绪,直到看了评论
- 评论说到快慢指针的问题,快指针比慢 指针 快 n步,这样就清楚了
使用while循环判断是否为空写下去
2.1 遇到的问题
- 在学习数据结构的时候,有个头结点,头结点类似是个虚的,专门指向第一个结点也是用的
- 我在做题时候,把这个虚头结点和 题目中的头结点搞混了,导致做题最后的slow.next.next指向是空指针,出错。
- 最后有一次看了评论,发现题中 的head就是第一个有数据的结点,我们需要的是那个虚的头结点。这样,就明白了了自己有的地方错误了;
2.1.1自己开始写遇到的问题
-
思考:双指针算不算走了两次?
我在看评论的时候,看到了一个c++题解,我用了他的思想,但是还是有问题,但是不知道怎么解决。 我把这个问题记录下来。 代码:
/**
* 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 fast =head;
ListNode slow = head;
for(int i = 0 ; i < n ; i ++)
{
fast=fast.next;
}
while(fast.next!=null)
{
fast=fast.next;
slow=slow.next;
}
slow.next =slow.next.next;
return head;
}
}
上面这个代码 ,看起来没什么问题,但是在测试案例:
[1,2]
2
[1]
1
这样就会空指针异常,我尝试去增加if语句来拦截这样的错误,但始终没有解决
我觉得这是我没有把头结点问题解决好。
我就尝试着把上面的代码改进:
/**
* 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 realhead = new ListNode(0,head);
ListNode fast =realhead;
ListNode slow = realhead;
for(int i = 0 ; i < n ; i ++)
{
fast=fast.next;
}
if(fast==null)
{
for(int j = 0 ; j < n-1;j++)
{
slow =slow.next;
}
return realhead;
}
while(fast.next!=null)
{
fast=fast.next;
slow=slow.next;
}
slow.next =slow.next.next;
return realhead.next;
}
}
- 先让fast走,害怕出现走到末尾,加一个if判断,那if里面的就是fast走到最后的那种情况,这样的话,我们需要再循环一下slow,让slow少走一步,就行了,最后返回。
- 果然问题还是出现在头结点上。
2.2改进后代码
现在来写一下代码,虽然代码看起来不难,但是我自己写的时候,老出错。提示空指针异常。这也是头指针没搞清楚的原因。
/**
* 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 realHead = new ListNode(0,head);//说明realHead.next = head; 值为0,值为什么无所谓,起到指向头的位置就行
//定义快慢指针
ListNode fast = realHead;
ListNode slow = realHead;
int i = 0 ;
while(fast.next!= null)
{
if(i<n)//先让fast到第n个位置
{
i++;
fast = fast.next;
continue;
}
//两个指针在一起走
slow = slow.next;
fast = fast.next;
}
//出了while循环就算fast走完了 这样slow就走到了倒数第n的前驱
slow.next = slow.next.next;
return realHead.next;
}
}
3.总结
自己在写的时候,遇到的问题都在2.1之中了,写这个博客先把2.2都写完了后,又返回去思考了2.1.1的问题,没想到,加了头结点后果然解决了,但是这样似乎没有遍历一次,双指针应该算是遍历一次,本题考的是数据结构中的题,不难,自己在数据结构中遇到过,虽然知道思想,写的时候还是差了点水平,本题的关键是他给的头结点让我觉得是那个虚的头结点,我反而出错了,审题也是关键。