LeetCode 刷题笔记【19. 删除链表的倒数第 N 个节点】

Posted by Leonsux on April 21, 2022

题目

https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

给你一个链表,删除链表的倒数第 n个结点,并且返回链表的头结点。

image.png

示例

输入: head = [1,2,3,4,5], n = 2

输出: [1,2,3,5]

输入: head = [1], n = 1

输出: []

解题思路

删除第 N 个节点很简单,找到第 N - 1 个节点,让他的 next 指向他 next 的 next即可,current.next = current.next.next

所以这题的关键点在于 如何找到一个链表的倒数第 N 个节点

找到链表的第 N 个节点很简单,从头遍历即可,找倒数第 N 个结点也不复杂,如果知道了链表的长度 length,就可以转换为招链表的第 length - N 个节点了。

所以这一题可以先遍历一边链表,得到长度,再遍历第二遍,找到第 N - 1个节点即可,但这样需要两次遍历,显然不够优雅。

换个思路,仔细观察一下链表,目标节点到链表结尾的距离为 N ,那我们找到正数距离为 N 的位置,记为 fast,头节点记为 slow,他们之间的距离 为 N,那么当他们同时往后遍历,并且 fast 遍历到结尾时,slow 正好是目标位置,没错,就是 双指针

image.png

这里我们按照上面思路封装一个方法,用来获取链表的倒数第 N 个节点,以后有需求可以直接拿来用。

const findLastN = (list, n) => {
  let fast = list;
  let slow = list;
  let count = 0;
  while(fast) {
    if (count >= n) {
      slow = slow.next;
    }
    count++;
    fast = fast.next;
  }
  return slow;
}

不过本题还要考虑一个特殊情况:删除头节点。按照解题思路,我们需要获取倒数第 N + 1 个节点,而头节点的前面没有节点了,所以解法也很简单,给头节点的前面再添加一个节点(定义为 dummy)即可,操作的时候使用 dummy,最终返回 dummy.next 即可。

代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} n
 * @return {ListNode}
 */
var removeNthFromEnd = function(head, n) {
  const findLastN = (list, n) => {
    let fast = list;
    let slow = list;
    let count = 0;
    while(fast) {
      if (count >= n) {
        slow = slow.next;
      }
      count++;
      fast = fast.next;
    }
    return slow;
  }
  const dummy = new ListNode(0, head);
  const preN = findLastN(dummy, n + 1);

  preN.next = preN.next.next;
  return dummy.next;
};