• Home
  • About
    • LiaX photo

      LiaX

      Running in my time zone

    • Learn More
    • LinkedIn
    • Github
  • Posts
    • All Posts
    • All Tags
  • Projects

19. Remove Nth Node From End of List

25 May 2025

Reading time ~3 minutes

Intuition: The Ruler Metaphor

Think of a ruler of fixed length n, where:

  • The left endpoint starts at the head of the list.
  • The right endpoint is at the n-th node from the head.

Now slide this ruler one node at a time toward the right until the right endpoint reaches the end of the list.

At that moment: The left endpoint is exactly at the (length - n)-th node — that is, the node just before the one we need to remove.

This is the essence of the two-pointer approach, where:

  • One pointer (fast) is advanced n steps ahead of the other (slow).
  • Then both pointers move forward in lockstep.
  • When fast reaches the end, slow is just before the target.

The Edge Case: Removing the Head Node

Now comes a subtle point: If n == length of list, then the node to be removed is the head itself. This causes a problem: The node before the one we want to delete doesn’t exist. Without access to the previous node, we can’t reassign .next to skip the target.

=> We can use a simple but powerful trick: introduce a dummy (sentinel) node at the start of the list.

Solution

 * 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) {
        ListNode dummy = new ListNode(0, head);
        ListNode fast = dummy, slow = dummy;

        for(int i = 0; i < n; i++){
            fast = fast.next;
        }
        // Move both pointers until 'fast' reaches the end of the list
        while(fast.next != null){
            fast = fast.next;
            slow= slow.next;
        }

        slow.next = slow.next.next;
        /*
        Important: returning 'head' here would fail 
        when the original head is removed
        */
        return dummy.next; 
    }
}
Time Complexity: O(N)
Space Complexity: O(1)

Should I use while (node) or while (node.next) in my loop?

  • (1) while (node)
    • while (node) checks if the current node exists. It raverse all nodes, including the last node
    • e.g. Printing All Node Values
        while (node != null) {
         System.out.println(node.val);
         node = node.next;
        }
      

      We need to print every value, including the last node. So node must reach the end and process the node whose next == null.

  • (2) while (node.next)
    • while (node.next) checks if there is a next node available, which is useful if you plan to access or modify node.next. It traverse up to the node before a target node
    • e.g. Deleting the Next Node ```java while (node.next != null) {

      if (node.next.val == target) { node.next = node.next.next; } else { node = node.next; }

    } ``` Here we must stop at the node before the one we want to remove, so we can safely access and reassign node.next.

Why should I return dummy.next instead of head when using a dummy (sentinel) node?

The key lies in understanding what head and dummy represent, and how modifying the list (especially when deleting the head node) affects them.

ListNode dummy = new ListNode(0, head);  
// dummy.next = head
  • head is a reference to the original head node.
  • dummy is a new node that points to head, acting as a fixed, external anchor.

If the first node (i.e., the original head) gets deleted, head still points to the now-deleted node, and returning it is incorrect. However, dummy.next always reflects the updated head of the modified list.



Share Tweet +1