【LeetCode】详解删除链表的倒数第N个节点19. Remove Nth Node From End of ListGiven a linked list, remove the n-th


前言

一开始我以为今天这道题很简单,也很快通过了,但是看了一下讨论区的代码,怎么都跟我的不一样,后来才发现原来我的英文翻译错了,具体是怎么回事呢?接着往下看吧


正文

原题:

链接:删除链表的倒数第N个节点

Given a linked list, remove the n-th node from the end of list and return its head.
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,让你删除链表的倒数第N个节点,比如链表1 -> 2 -> 3 -> 4 ->5n为2,则删除倒数第2个节点4,并将链表的头节点返回,返回之后的链表应该变成1 -> 2 -> 3 -> 5

思路1:

我心想:挺简单的啊,用一个List存储链表的每个节点,然后直接remove倒数第N个元素,并将List赋值给链表不就行了,如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码

public ListNode removeNthFromEnd(ListNode head, int n) {
    List<Integer> list = new ArrayList<>();
    //向list中添加链表元素
    while (head != null) {
        list.add(head.val);
        head = head.next;
    }
    //list删除倒数第n个元素
    list.remove(list.size() - n);
    //若list为空了,就无需继续判断,直接返回null
    if (list.size() == 0) {
        return null;
    }
    //新建一个临时的链表,并指向head
    ListNode temp = new ListNode(0);
    head = temp;
    //往temp中添加list的元素
    for (int i = 0; i < list.size(); i++) {
        temp.val = list.get(i);
        if (i != list.size() - 1) {
            temp.next = new ListNode(0);
            temp = temp.next;
        }
    }
    return head;
}

代码讲解
以上代码应该很好懂,思路就是用list存储链表的每个节点,删除之后赋值给链表,提交代码也成功通过了,但是总感觉不对劲,这是一道中等难度的题,不可能这么简单吧
在这里插入图片描述
这时候我注意到了题目中的Follow up,一开始我把它翻译成你能一次通过吗?,但是我查看了中文版的leetcode,才发现是另外一种翻译
在这里插入图片描述
在这里插入图片描述
如上图所示,中文版leetcode把它翻译成你能尝试使用一趟扫描实现吗?,原来这才是重点!
刚刚的解法中,我使用了两趟扫描,一趟扫描链表节点、一趟扫描重新赋值,那如果只使用一趟扫描需要怎么做呢?

思路2:

其实第一种做法中使用list存储的一个目的就是为了记录链表的长度,从而可以方便地删除倒数第n个节点,那如果只用一趟扫描的话,list肯定不能用了。这时候我们可以从如何找到倒数第n个节点的角度出发,首先看一下下面这张图,图中链表标出了两个节点,其中一个节点是第n个节点,另外一个节点是倒数第n个节点:
在这里插入图片描述
我们可以得知head到n1的距离等于n2到rail的距离,借助两个指针p1、p2,其中p1指向head、p2指向n1,如下图所示:
在这里插入图片描述
让p1和p2同时移动,直到p2指向最后一个节点,此时p1会指向哪里呢(建议同学思考一下)?
没错,p1指向了n2的位置,也就是倒数第n个位置,如下图所示:
在这里插入图片描述
这是为什么呢?其实很简单,由图中得知head到n1的距离等于n2到rail的距离,因此head到n2的距离也就等于n1到rail的距离,所以让它们同时移动且p2到达rail位置时,p1就可以指向n2的位置,也就是倒数第n个位置!
在这里插入图片描述
有一点需要注意的,题目让我们删除倒数第n个节点,但是我们不需要找倒数第n个节点,而是要找倒数第n + 1个节点,因为删除倒数第n个节点的步骤是将第n + 1个节点的next指针指向第n - 1个节点,我们不需要找到第n个节点,如下图所示:
在这里插入图片描述
代码

public ListNode removeNthFromEnd(ListNode head, int n) {
	//这里看作是头节点,由于原链表head是没有头节点的,操作起来会比较麻烦,所以需要新增一个头节点
    ListNode newHead = new ListNode(0);
    ListNode p1 = newHead;
    ListNode p2 = newHead;
    //此时的p2就变成了一个拥有头节点的head链表
    p2.next = head;
    //将p2移动到第n个节点(相对于原链表而言)
    for (int i = 0; i < n; i++) {
        p2 = p2.next;
    }
    //p1、p2同时移动,直到p2指向最后一个节点,p1指向了倒数第n+1个节点
    while (p2.next != null) {
        p2 = p2.next;
        p1 = p1.next;
    }
    p1.next = p1.next.next;
    return newHead.next;
}

代码讲解
由于原题没有给头节点,这会导致我们在查找节点时出现一些麻烦,因此我们新建了一个newHead的头节点,并让p1跟p2指向newHead,p2.next = head; 这一句代码使p2变成了一个拥有头节点的head链表,假设n = 2,原链表为

1 -> 2 -> 3 -> 4 -> 5

那么p2就变成了

0 -> 1 -> 2 -> 3 -> 4 -> 5

for (int i = 0; i < n; i++) {
    p2 = p2.next;
}

上面的代码将p2移动到第n个节点(相对于原链表而言),即移动到了元素为2的节点
在这里插入图片描述

while (p2.next != null) {
    p2 = p2.next;
    p1 = p1.next;
}

这段代码的作用就是让p1、p2同时移动,直到p2移动到最后一个元素停止
在这里插入图片描述
此时p1移动到了倒数第n+1个元素的位置,将倒数第n个元素从链表中删除,即

p1.next = p1.next.next;

在这里插入图片描述
提交代码!KO!
在这里插入图片描述


总结

一开始我以为只使用英文版的leetcode就行了,直到今天因为英文翻译的问题,误解了题目的意思,这时才意识到中英版本的leetcode可以互相参考,平时刷题可以用英文的,若实在不懂的话再查看中文版本的作为辅助,或许这样就可以避免一些问题了。
好了,这就是今天的题了,送给大家一句话:
有些事情本来很遥远,你争取,它就会离你愈来愈近!——来源《破风》
共勉!

发布了57 篇原创文章 · 获赞 282 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_41463193/article/details/92017579