开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第6天,点击查看活动详情
前言
本系列文章主要会总结一些常见的算法题目以及算法的易错点,难点,以及一些万用的公式,并且结合实际的 Leetcode 题目来进行加深理解以及实际应用,算法这种东西,属于是一到用时方恨少的类型,在平时总结一些常见的简单算法,经常磨练自己的算法思维,对于日常的开发还是能有不少的帮助的。
- 今天来介绍一下删除倒数第N个节点的操作
怎么删除倒数第N个节点
下面是一个正常的链表,里面有四个元素,最后指向null
假设现在要删除倒数第二个节点,按照正常的思路来说,要遍历两遍链表,第一遍可以得到链表的整个长度,第二遍找到倒数第二个的位置,并且在倒数第三个节点上进行操作,删除掉倒数第二个节点,删除的操作为改变上一个节点的next指向,就可以删除当前节点。
那么现在有一个问题,我们能否通过一次遍历就找到倒数第N个节点,节省一次遍历的性能呢?
当然是可以的,这里就是经典的双指针的使用,再次之前,因为题目可能会存在删除第一个节点的情况,所以为了统一所有节点的处理,我们还是优先设定虚拟头节点 dummyNode,不清楚为什么的可以优先移步之前的题目
在用虚拟头节点统一了处理以后,我们就可以开始定义双指针,首先我们需要定义两个快慢指针 slow 和 fast ,他们之间的距离就是我们需要删除的倒数第N个节点的 N 在加上 1 ,至于为什么要加1 这个后面看图就可以明白。
就拿上面的倒数第二个节点来做个例子。
初始化的时候两个指针的距离为 3 也就是 2 + 1 ,这样,两个指针一直同步往后移动,直到快指针指向null的时候,就会发现,慢指针指向的刚好就是我们需要删除的节点的前一个节点,这也就是为什么要加1的原因,可以比较方便现在的删除环节。
然后就是按照之前的删除步骤,将慢指针的指向从下一个节点改为下下个节点,这里要注意是不是最后一个节点。
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;
}