Remove Nth Node From End of List

You are given the head of a singly linked list and an integer n. Delete the node that is n-th when counting from the end of the list (the last node is 1st from the end, the one before it is 2nd, and so on), then return the head of the resulting list.

The list is shown as an array of its values from head to tail, e.g. [10, 20, 30]. If the head itself is removed, return the new first node, or null if the list becomes empty.

Can you do it in a single pass over the list?

Example 1

Input: head = [10,20,30,40,50], n = 2 Output: [10,20,30,50] Why: Counting from the end, 50 is 1st and 40 is 2nd, so 40 is removed.

Example 2

Input: head = [7], n = 1 Output: [] Why: Removing the only node leaves an empty list.

Example 3

Input: head = [4,8], n = 2 Output: [8] Why: With two nodes, the 2nd from the end is the head itself, so the new head is 8.

Constraints

  • The number of nodes sz satisfies 1 <= sz <= 104
  • -106 <= Node.val <= 106
  • 1 <= n <= sz

Pattern Path · Linked Lists · problem 71 of 169

With two passes this is easy: count the nodes, then walk to the one just before the target. How could you know where "n from the end" is without counting first?
Use two pointers that move at the same speed but start n nodes apart. When the front one falls off the end, where is the back one?
Start both pointers at a dummy node placed before the head. Then the back pointer stops right before the node to delete, and deleting the head needs no special case.
Target: O(L) time, O(1) extra space.

Start simple

Count the length L in one pass, then walk L - n steps to the predecessor and unlink it: O(L) time but two passes.

The key idea

To delete a node in a singly linked list you need its predecessor, so the real task is to stop one node before the target. Two pointers with a fixed gap give you that in a single pass.

Create a dummy node whose next is the head, and set fast and slow to the dummy. Move fast forward n steps. Now move both together until fast.next is null, i.e. fast sits on the last node. The gap between them is still n, so slow is exactly n nodes before the last node: it is the predecessor of the n-th node from the end. Unlink with slow.next = slow.next.next and return dummy.next.

The dummy node is what makes removing the head painless: in that case slow never moves, and dummy.next automatically becomes the second node.

Complexity

  • Time: O(L)
  • Space: O(1)

Python

class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        dummy = ListNode(0, head)
        fast = slow = dummy
        for _ in range(n):  # open a gap of n nodes
            fast = fast.next
        while fast.next:    # when fast is on the last node, slow is just before the target
            fast = fast.next
            slow = slow.next
        slow.next = slow.next.next
        return dummy.next

JavaScript

var removeNthFromEnd = function(head, n) {
  const dummy = new ListNode(0, head);
  let fast = dummy, slow = dummy;
  for (let i = 0; i < n; i++) fast = fast.next; // open a gap of n nodes
  while (fast.next) {                           // when fast is on the last node, slow is just before the target
    fast = fast.next;
    slow = slow.next;
  }
  slow.next = slow.next.next;
  return dummy.next;
};

What the interviewer is checking

The interviewer is checking the two-pointer gap trick and careful pointer handling, especially the head-removal edge case that a dummy node solves.

Mistakes to avoid

  • Returning the original head when the head itself was removed.
  • Off-by-one in the gap: stopping on the target instead of its predecessor.
  • Dereferencing null when n equals the list length and no dummy node is used.

Follow-ups to expect

  • Return the n-th node from the end without deleting it. (Same gap, stop on target.)
  • What if n could be larger than the length? (Detect fast hitting null early.)
  • Find the middle node in one pass. (Fast moves two steps, slow one.)

In real software

Bounded history buffers and undo stacks often drop "the k-th most recent entry", and the fixed-gap pointer idea is how streaming systems track a trailing window without knowing the total length.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url