算法合集 | 链表 | Leetcode 19.删除链表的倒数第N个节点

86 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情

前言

本系列文章主要会总结一些常见的算法题目以及算法的易错点,难点,以及一些万用的公式,并且结合实际的 Leetcode 题目来进行加深理解以及实际应用,算法这种东西,属于是一到用时方恨少的类型,在平时总结一些常见的简单算法,经常磨练自己的算法思维,对于日常的开发还是能有不少的帮助的。

  • 今天来介绍一下删除倒数第N个节点的操作

怎么删除倒数第N个节点

下面是一个正常的链表,里面有四个元素,最后指向null

图片.png

假设现在要删除倒数第二个节点,按照正常的思路来说,要遍历两遍链表,第一遍可以得到链表的整个长度,第二遍找到倒数第二个的位置,并且在倒数第三个节点上进行操作,删除掉倒数第二个节点,删除的操作为改变上一个节点的next指向,就可以删除当前节点。

image.png

那么现在有一个问题,我们能否通过一次遍历就找到倒数第N个节点,节省一次遍历的性能呢?

当然是可以的,这里就是经典的双指针的使用,再次之前,因为题目可能会存在删除第一个节点的情况,所以为了统一所有节点的处理,我们还是优先设定虚拟头节点 dummyNode,不清楚为什么的可以优先移步之前的题目

算法合集 | 链表 | Leetcode 24. 两两交换链表中的节点

在用虚拟头节点统一了处理以后,我们就可以开始定义双指针,首先我们需要定义两个快慢指针 slow 和 fast ,他们之间的距离就是我们需要删除的倒数第N个节点的 N 在加上 1 ,至于为什么要加1 这个后面看图就可以明白。

就拿上面的倒数第二个节点来做个例子。

image.png

初始化的时候两个指针的距离为 3 也就是 2 + 1 ,这样,两个指针一直同步往后移动,直到快指针指向null的时候,就会发现,慢指针指向的刚好就是我们需要删除的节点的前一个节点,这也就是为什么要加1的原因,可以比较方便现在的删除环节。

image.png

然后就是按照之前的删除步骤,将慢指针的指向从下一个节点改为下下个节点,这里要注意是不是最后一个节点。

Leetcode 19.删除链表的倒数第N个节点

可以通过获取到的倒数第n个结点的这个n进行递减,来获取fast指针的起始位置。如下:

while(n--) {
    fast = fast.next!;
}

然后一直循环fast指针的下一节点,直到指针为空,删除慢指针指向节点的下一节点,就完成这道题目了

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     val: number
 *     next: ListNode | null
 *     constructor(val?: number, next?: ListNode | null) {
 *         this.val = (val===undefined ? 0 : val)
 *         this.next = (next===undefined ? null : next)
 *     }
 * }
 */
  
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    let dummyNode = new ListNode(0, head);
    let slow = dummyNode;
    let fast = dummyNode;
  
    while(n--) {
        fast = fast.next!;
    }
    while(fast.next) {  
        fast = fast.next;
        slow = slow.next!;
    }
    slow.next = slow.next.next; 
    return dummyNode.next; 
}

图片.png