Remove Nth Node From End of List
Difficulty: Medium
You're given the head of a linked list and a number n. Remove the node that sits n positions from the end of the list (so n = 1 means remove the very last node), then return the head of the resulting list.
Try to do it by walking the list only once.
Examples
Input: head = [1, 2, 3, 4, 5], n = 2
Output: [1, 2, 3, 5]
The 2nd node from the end is the one with value 4.
Input: head = [1], n = 1
Output: []
Input: head = [1, 2], n = 1
Output: [1]
Constraints
The number of nodes is between 1 and 30.
0 <= Node.val <= 100
1 <= n <= number of nodes in the list
Approach
If you first walk the whole list once just to count its length, then "n from the end" becomes a plain, fixed position counted from the front — you can walk to just before that position and unlink the node after it. That's correct, but it takes two full passes.
You can fold both passes into one: start two pointers together, but advance one of them (the "fast" pointer) n steps ahead before moving the other pointer at all. From then on, move both pointers one step at a time. When the fast pointer reaches the end of the list, the gap you built in means the slow pointer is sitting exactly one node before the one that needs to be removed.
Solutions
Brute Force — Two Passes (Count, Then Remove)
Walk the list once just to count how many nodes it has. That count tells you exactly how many steps in from the front the target node sits. Walk the list a second time to that position and unlink the node there. A dummy node placed before the head makes removing the very first node no different from removing any other.
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function removeNthFromEnd(head, n) {
let length = 0;
let curr = head;
while (curr) {
length++;
curr = curr.next;
}
const dummy = new ListNode(0, head);
let prev = dummy;
for (let i = 0; i < length - n; i++) {
prev = prev.next;
}
prev.next = prev.next.next;
return dummy.next;
}Time: O(n) — two passes over the list · Space: O(1)
Optimal — One Pass with a Gap
Attach a dummy node in front of the head so removing the real head node needs no special case. Advance a "fast" pointer n steps ahead of a "slow" pointer, both starting at the dummy node. Then move both pointers forward together, one step at a time, until fast reaches the last node. Because of the n-node gap you built in, slow is now sitting exactly one node before the one to remove, so you can unlink it directly.
class ListNode {
constructor(val, next = null) {
this.val = val;
this.next = next;
}
}
function removeNthFromEnd(head, n) {
const dummy = new ListNode(0, head);
let fast = dummy;
let slow = dummy;
for (let i = 0; i < n; i++) {
fast = fast.next;
}
while (fast.next) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}Time: O(n) · Space: O(1)