您的位置:首页 > Web前端 > Node.js

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

2020-03-27 19:16 996 查看

文章目录

  • 总结
  • 前言

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

    正文

    原题:

    链接:删除链表的倒数第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可以互相参考,平时刷题可以用英文的,若实在不懂的话再查看中文版本的作为辅助,或许这样就可以避免一些问题了。
    好了,这就是今天的题了,送给大家一句话:
    有些事情本来很遥远,你争取,它就会离你愈来愈近!——来源《破风》
    共勉!

    • 点赞
    • 收藏
    • 分享
    • 文章举报
    Hertter 发布了57 篇原创文章 · 获赞 299 · 访问量 7万+ 私信 关注
    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐