LC.19. 删除链表的倒数第 N 个结点

本文介绍了如何在链表中删除倒数第n个节点,详细解析了错误思路和正确解法。错误思路是先找节点值再删除,对于值重复的情况不适用。正确解法分为两种:一是通过节点地址删除,二是利用虚拟头节点直接删除。这两种方法都避免了遍历链表多次,提高了效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点:

  1. 寻找链表倒数第n个节点:采用双指针法,先让快指针走 n 步,之后让快、慢指针一起走,当快指针为空时,慢指针所指即为倒数第 n 个节点。
  2. 删除链表中某一节点的思路:单链表中,一个节点的删除需要借助其前一个节点,这样才能保证删除后链表的完整、连续。但头节点前没有节点,所以与其他节点不同,需要额外处理。若头节点需要删除,只需将 head 指向 head.next 即可。此外,也可以设置一个虚拟头节点 dummyHead ,以此就可以按照统一的逻辑来删除节点了,但要注意的是,最后的返回值为 dummyHead.next
  3. 虚拟头节点的应用大大简化了链表添加、删除的操作。
  4. 判断链表节点值相等,使用 == ;判断链表节点地址相等,使用 equals 。

 

错误思路:

  二刷该题时,觉得本题更像是 寻找链表倒数第 n 个节点删除链表中节点 的结合体。可以先找出第 n 个节点的节点值记为 res ,然后再删除链表中值为 res 的节点。但是对于用例 [4,5,4] 1,则无法通过,因为代码在遇到第一个4就删除了,但是我们按要求要删的是第二个4,所以该方法存在缺陷。

 

正确思路:

  解法一:修改自上述错误思路,既然通过节点值无法正确删除节点(节点值会有重复),那么我们可以考虑通过节点独一无二的特征去删除,比如节点地址。在找到链表倒数第 n 个节点后,记录其节点地址,然后再次遍历链表,找到相同节点地址的节点进行删除。

  代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode slow, fast;
        slow = fast = head;
        // 先找出第n个节点值
        while(n --> 0) {
            fast = fast.next;
        }
        while(fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        ListNode target = slow; // 要删除的节点

        // 删除链表中 target 节点
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        slow = dummyHead;
        while(slow.next != null) {
            if(slow.next.equals(target)) {
                slow.next = slow.next.next;
            }else {
                slow = slow.next;
            }
            
        }
        return dummyHead.next;
    }
}

  解法二:在找到链表中倒数第 n 个节点后,直接进行删除即可,并不需要记录其节点地址,继而再次遍历,这样的话,通过一次遍历就解决本题。思路简单,但仍要注意其中的很多细节。

  步骤如下:

  1. 定义虚拟头节点 dummyHead(简化删除操作)。
  2. 定义快慢指针,fast、slow,均指向 dummyHead 。
  3. fast 指针先走 n + 1 步,因为只有这样,同时移动的时候 slow 指针才能指向要删除节点的上一个节点。
  4. fast、slow 同时移动。
  5. 删除 slow 指向的下一个节点。

  代码如下:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head;
        ListNode slow, fast;
        slow = fast = dummyHead;
        // fast先走n + 1步
        while(n --> 0) {
            fast = fast.next;
        }
        fast = fast.next;

        // fast、slow同时走,找到删除节点的上一个节点的位置,即slow
        while(fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        
        slow.next = slow.next.next;
        return dummyHead.next;
    }
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值