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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
};