150NeetCode 150

38.Remove Nth Node From End of List

MediumBlind 75Linked List ยท 4/11

Problem

Given the head of a linked list, remove the n-th node from the end of the list and return the head of the modified list.

n is always valid (1 <= n <= length). Note that if n equals the length, the node to remove is the head itself.

Examples

Input:  head = [1, 2, 3, 4, 5], n = 2
Output: [1, 2, 3, 5]
Explanation: The 2nd node from the end is 4.
Input:  head = [1], n = 1
Output: []
Explanation: Removing the only node leaves an empty list.
Input:  head = [1, 2], n = 1
Output: [1]

Constraints

  • The number of nodes in the list is sz.
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

๐Ÿ’ก Key Insight

To delete a node in a singly linked list you need the node before it. If two pointers start together and the fast one is moved n + 1 steps ahead, then when fast falls off the end, slow sits exactly one node before the target โ€” giving a single pass. A dummy node before head makes removing the head itself no different from any other removal.

Brute Force (Array of Nodes)

TimeO(L)SpaceO(L)

Intuition

Linked lists lack random access, so copy the node references into an array. Then the target is at index size - n and its predecessor at size - n - 1.

Algorithm

  1. Collect every node into nodes.
  2. idx = size - n is the node to remove.
  3. If idx == 0, return head.next (removing the head).
  4. Otherwise nodes[idx - 1].next = nodes[idx].next and return head.

Visualization

nodes: [1, 2, 3, 4, 5], n = 2
idx = 5 - 2 = 3  -> node 4
nodes[2] (3).next = nodes[3].next (5)
result: 1 -> 2 -> 3 -> 5

Code

java
import java.util.ArrayList;
import java.util.List;

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        List<ListNode> nodes = new ArrayList<>();
        for (ListNode node = head; node != null; node = node.next) {
            nodes.add(node);
        }
        int idx = nodes.size() - n; // index of the node to delete
        if (idx == 0) {
            return head.next; // deleting the head
        }
        nodes.get(idx - 1).next = nodes.get(idx).next;
        return head;
    }
}

Complexity

  • Time: O(L) โ€” one pass over the L nodes.
  • Space: O(L) โ€” the array of references.

Complexity Comparison

Approach Time Space
Array of Nodes O(L) O(L)
Two Passes O(L) O(1)
One Pass Two Pointers O(L) O(1)

Common Pitfalls

  • Not using a dummy node, then crashing (or returning the wrong head) when n equals the list length and the head must be removed.
  • Moving fast only n steps instead of n + 1 from the dummy โ€” slow then lands on the target instead of its predecessor.
  • Returning head instead of dummy.next โ€” if the head was deleted, head is stale.
  • Off-by-one in the two-pass version: the predecessor is length - n steps from the dummy, not from head.