Linked List · LC #19

Remove Nth Node From End

Remove the nth node from the end of a linked list in one pass using a two-pointer gap technique. The dummy node handles head-removal edge cases cleanly.

mediumLC #19AMZ★★GOOMETA★★MSFT★★AAPL
Mark as done
Confidence

Statement

Problem & Approach

Problem statement, sample I/O, and key reasoning.

Given the head of a linked list, remove the nth node from the end of the list and return its head.

Sample I/O

Input:  head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]

Input:  head = [1], n = 1
Output: []

Intuition

How to Think About It

Intuition

If we advance a fast pointer exactly n+1 steps ahead of a slow pointer, both pointers move together until fast falls off the end of the list. At that point slow is exactly one node before the target - perfectly positioned to skip it with slow.next = slow.next.next. The dummy node before head ensures this works even when removing the head node itself.

Core Concept

Create dummy node pointing to head. Initialise fast = slow = dummy. Advance fast exactly n+1 times. Then advance both until fast is null. Now slow.next is the nth-from-end node. Execute slow.next = slow.next.next. Return dummy.next. The n+1 offset (not n) is critical: it lands slow at the predecessor of the target, not the target itself. Time: O(L), Space: O(1).

When to use

Any "kth from end" problem on linked lists; also a sub-pattern when you need to position a pointer relative to the list tail in one pass.

When NOT to use

When the list is doubly linked (can traverse backwards). When n is guaranteed to equal the list length - just return head.next.

Key Insights

What to Know Cold

  • Advance fast n+1 steps (not n) so slow lands at the predecessor of the target, enabling deletion.
  • Dummy node before head unifies the edge case of removing the head - no special-casing needed.
  • Both pointers start from dummy, not head - keeps the offset math consistent.
  • n is guaranteed valid (1 ≤ n ≤ length) per constraints, so no bounds check needed.
  • Alternative: two-pass approach - count length, then advance (L-n) steps. O(L) but two passes.

1 example

Worked Examples

Remove 2nd from end of 5-node list

[1,2,3,4,5] n=2 → remove node 4

Dummy + n+1 gap between fast and slow

function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
    const dummy = new ListNode(0, head);
    let fast: ListNode | null = dummy, slow: ListNode | null = dummy;
    for (let i = 0; i <= n; i++) fast = fast!.next;
    while (fast) { slow = slow!.next; fast = fast.next; }
    slow!.next = slow!.next!.next;
    return dummy.next;
}

Output: [1,2,3,5]

Classic one-pass gap technique - also used to find kth-from-end node in general.