Remove Nth Node From End of List: A Complete Guide
If you’ve ever prepared for a technical interview, you’ve likely encountered the classic linked list problem: remove nth node from end of list. But beyond interviews, this problem is a great exercise in writing clean, efficient code. It’s a favorite among interviewers because it tests your understanding of linked list traversal, edge cases, and the subtle art of using two pointers. In this article, we’ll break down the problem step by step, explore two solid approaches, and make sure you walk away with a deep understanding—not just a memorized solution Still holds up..
Understanding the Problem Statement
Before diving into code, let’s clearly define what we’re trying to achieve. You are given a singly linked list and an integer n. Your task is to remove the n-th node from the end of the list and return the head of the modified list.
This changes depending on context. Keep that in mind Simple, but easy to overlook..
Take this: consider the linked list:
1 -> 2 -> 3 -> 4 -> 5
If n = 2, the node to remove is the second from the end, which is 4. The resulting list becomes:
1 -> 2 -> 3 -> 5
Seems simple, right? But there are a few important constraints to keep in mind:
- The list is singly linked, meaning you can only traverse forward.
- The value of
nis always valid (i.e., it won’t be zero or larger than the number of nodes). - You need to handle the case where the node to remove is the head itself.
Now, let’s explore two main approaches to solve this problem: the straightforward two-pass method and the more elegant one-pass two-pointer technique Practical, not theoretical..
Approach 1: The Two-Pass Algorithm
The most intuitive way to solve this problem is to use two passes through the list. On the flip side, then, calculate the position of the target node from the start. The idea is simple: first, find the length of the list. Finally, traverse to the node just before the target and adjust its next pointer.
Not obvious, but once you see it — you'll see it everywhere.
Steps for the Two-Pass Method
- Find the length of the linked list by traversing from head to tail. Let’s call this
length. - Compute the index of the node to remove from the start:
index_to_remove = length - n. This is the zero-based position from the head. - Handle the head removal case: If
index_to_remove == 0, the head itself must be removed. Simply returnhead.next. - Traverse to the node before the target: Move
index_to_remove - 1steps from the head to reach the previous node. - Update the pointer: Set
prev.next = prev.next.nextto skip the target node. - Return the head of the modified list.
Here’s how this looks in Python:
def removeNthFromEnd(head, n):
# Step 1: Find the length of the list
length = 0
current = head
while current:
length += 1
current = current.next
# Step 2: Compute the index from the start
index_to_remove = length - n
# Step 3: If removing the head
if index_to_remove == 0:
return head.next
# Step 4: Traverse to the node before the target
prev = head
for _ in range(index_to_remove - 1):
prev = prev.next
# Step 5: Skip the target node
prev.next = prev.next.next
# Step 6: Return the head
return head
Time and Space Complexity
- Time complexity: O(L), where L is the length of the list. We traverse the list twice, but each pass is linear, so the total is still O(L).
- Space complexity: O(1), since we only use a constant amount of extra space (a few pointers).
The two-pass approach is easy to understand and implement, but it does require two full traversals. Can we do better? Yes—by using the two-pointer technique.
Approach 2: One-Pass with Two Pointers
The two-pointer method is more efficient in practice because it only requires a single traversal. The core idea is to maintain a gap of n nodes between two pointers. When the fast pointer reaches the end of the list, the slow pointer will be exactly at the node before the one we want to remove That's the part that actually makes a difference..
Steps for the Two-Pointer Method
- Create a dummy node that points to the head. This helps handle edge cases where the head is removed, and it simplifies the code.
- Initialize two pointers:
fastandslow, both starting at the dummy node. - Move
fastahead byn + 1nodes. Whyn + 1? Because we wantslowto point to the node before the target whenfastreaches the end. This way, we can easily unlink the target node. - Move both pointers one step at a time until
fastbecomesNone. At this point,slow.nextis the node to remove. - Update the pointer: Set
slow.next = slow.next.nextto skip the target. - Return
dummy.nextas the new head.
Let’s see the code:
def removeNthFromEnd(head, n):
dummy = ListNode(0, head) # dummy node pointing to head
fast = slow = dummy
# Move fast ahead by n+1 nodes
for _ in range(n + 1):
fast = fast.next
# Move both pointers until fast reaches the end
while fast:
fast = fast.next
slow = slow.next
# Skip the target node
slow.next = slow.next.next
return dummy.next
Why Does This Work?
The key is the fixed gap of n nodes between fast and slow. That's why nextis then-th node from the end. When fastreaches the end (i.e.Worth adding: ,None), slowis exactlynnodes behind. This leads to since we startedslowat the dummy node,slow. This elegant trick eliminates the need to know the list length in advance.
Time and Space Complexity
- Time complexity: O(L), but we only traverse the list once. This is a constant-factor improvement over the two-pass method.
- Space complexity: O(1), as we only use a few extra pointers.
Edge Cases and Important Considerations
No matter which approach you choose, you must handle certain edge cases gracefully:
- Removing the head node: If
nequals the length of the list, the head is removed. The dummy node technique in the two-pointer method makes this trivial, asdummy.nextwill simply point to the second node. - List with only one node: If `n = 1
Completing the Single‑Node Scenario
When the list consists of a single element and n equals 1, the dummy node still points to the head, fast is advanced one step beyond the end (it becomes None), and slow remains at the dummy. The assignment slow.Still, next = slow. Also, next. Consider this: next therefore sets dummy. next to None, effectively returning an empty list. The function behaves exactly as intended without any extra branching The details matter here..
Handling Out‑of‑Range n Values
In a production‑ready implementation it is prudent to verify that n is within the valid range. If fast becomes None before the initial loop finishes (i.e.
- Raise an exception – signalling a misuse of the API, or
- Return the original list unchanged – keeping the operation safe but non‑destructive.
A concise check can be added right after the dummy setup:
# Verify that the list is long enough
for _ in range(n):
if fast is None: # not enough nodes
return head # or raise ValueError("n exceeds list length")
fast = fast.next
A Quick Test Harness
Below is a minimal driver that exercises the routine on several representative inputs, illustrating both the typical case and the edge conditions discussed:
def build_list(values):
dummy = ListNode(0)
cur = dummy
for v in values:
cur.next = ListNode(v)
cur = cur.next
return dummy.next
def to_pylist(head):
out = []
while head:
out.append(head.val)
head = head.
# 1. Normal case – remove middle element
head = build_list([10, 20, 30, 40, 50])
new_head = removeNthFromEnd(head, 2) # remove 40
print(to_pylist(new_head)) # [10, 20, 30, 50]
# 2. Remove the head
head = build_list([1, 2, 3])
new_head = removeNthFromEnd(head, 3) # remove 1
print(to_pylist(new_head)) # [2, 3]
# 3. Single‑node list
head = build_list([7])
new_head = removeNthFromEnd(head, 1) # remove 7
print(to_pylist(new_head)) # []
# 4. Invalid n (greater than length)
head = build_list([1, 2])
try:
new_head = removeNthFromEnd(head, 5)
except ValueError as e:
print(e) # n exceeds list length
Why the Two‑Pointer Technique Is Preferred
- Single traversal guarantees the smallest possible constant factor in runtime.
- No auxiliary data structures keep memory usage at a strict O(1).
- Uniform handling of all positions (head, middle, tail) through the dummy node eliminates special‑case code, making the implementation easier to audit and maintain.
Final Thoughts
The two‑pointer method delivers an elegant, linear‑time solution to the “remove Nth node from end of list” problem while keeping the codebase lean. That's why by introducing a dummy sentinel, establishing a fixed gap between two pointers, and performing a single pass, developers obtain a strong routine that gracefully manages edge cases such as head removal and single‑element lists. When combined with a simple length check for out‑of‑range n, the function becomes both safe and efficient, embodying the ideal balance of clarity and performance in linked‑list algorithms That's the part that actually makes a difference..