很容易想到的是先求出链表中元素的总个数,然后删除正数第n-k个元素,代码如下(下面的代码使用了虚拟头结点优化判断删除头结点的情况)

var removeNthFromEnd = function (head, n) {
  let count = 0;
  let cur = head;
  while (cur) {
    cur = cur.next;
    count++;
  }
  const dummyHead = new ListNode(-1);
  dummyHead.next = head;
  let prev = dummyHead;
  cur = head;
  let cnt = 0;
  while (cnt < count - n) {
    prev = cur;
    cur = cur.next;
    cnt++;
  }
  prev.next = cur.next;
  return dummyHead.next;
};

题目提示可以使用一次遍历的方法,这里百思不得其解,最后查看评论区使用了一种快慢指针。上述算法可以优化为只使用一次遍历。我们可以使用两个指针而不是一个指针。第一个指针从列表的开头向前移动 n+1 步,而第二个指针将从列表的开头出发。现在,这两个指针被 n 个结点分开。我们通过同时移动两个指针向前来保持这个恒定的间隔,直到第一个指针到达最后一个结点。此时第二个指针将指向从最后一个结点数起的第 n 个结点。我们重新链接第二个指针所引用的结点的 next 指针指向该结点的下下个结点。

image.png
image.png
var removeNthFromEnd = function (head, n) {
    // 快慢指针
    const dummyHead = new ListNode(-1);
    dummyHead.next = head;
    let first = dummyHead, second = dummyHead;
    for (let i = 1; i <= n + 1; i++) {
        first = first.next;
    }
    while (first) {
        first = first.next;
        second = second.next;
    }
    second.next = second.next.next;
    return dummyHead.next;
};