Description
Remove Nth Node From End of List
Given the head of a linked list, remove the nth node from the end of the list and return its head.
Example 1:

Input: head = [1,2,3,4,5], n = 2
Output: [1,2,3,5]
Example 2:
Input: head = [1], n = 1
Output: []
Example 3:
Input: head = [1,2], n = 1
Output: [1]
Constraints:
- The number of nodes in the list is
sz. 1 <= sz <= 300 <= Node.val <= 1001 <= n <= sz
Follow up: Could you do this in one pass?
Approach - 2 pointer
- Create left and right pointer and left pointing to a block before head and right pointing to the nth element
- Left pointer to a new block because if you start with head you will reach the exact element but we need to reach an element before that
- Shift right pointer to the right to the nth node so as to create a window then loop till it reaches null and move left pointer too it will reach n-1 node from last then it is simple
Time:O(n) Space:O(1)
/**
* 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 left = dummy;
ListNode right = head;
while (n > 0) {
right = right.next;
n--;
}
while (right != null) {
left = left.next;
right = right.next;
}
left.next = left.next.next;
return dummy.next; // cannot return head as [1] should return []
}
}To remove the -th node from the end of a singly-linked list, use either a two-pass length count or a two-pointer gap strategy.
Brute Force: Two-Pass Count Method
Find the total length of the list, then make a second pass to find and un-link the target node.
- Concept: If the total length is , the node to delete is at index from the front. The node right before it is at index .
- Algorithm:
- Traverse the entire list once to count total nodes .
- Create a
dummynode pointing toheadto safely handle deleting the head node. - Traverse steps from
dummyto reach the node before the target. - Skip the target node:
curr.next = curr.next.next.
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
int length = 0;
ListNode curr = head;
// Pass 1: Find length
while (curr != null) {
length++;
curr = curr.next;
}
// Pass 2: Stop right before target
curr = dummy;
for (int i = 0; i < length - n; i++) {
curr = curr.next;
}
// Delete target node
curr.next = curr.next.next;
return dummy.next;
}
}
- Time Complexity: (2 passes over the list).
- Space Complexity: .
Most Optimized & Easy to Remember: Fast & Slow Pointer Gap Method
Maintain a fixed gap of nodes between a fast and slow pointer so you can complete the removal in a single pass.
-
Mental Model (The Gap Rule):
-
If
fastis steps ahead ofslow, then whenfastreachesnull(end of list),slowwill naturally be sitting 1 node before the node to delete. -
Algorithm:
- Create a
dummynode pointing toheadand place bothfastandslowondummy. - Advance
fastforward by steps. - Move both
fastandslowforward one step at a time untilfasthitsnull. - Disconnect target node:
slow.next = slow.next.next.
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0, head);
ListNode fast = dummy;
ListNode slow = dummy;
// Step 1: Advance fast by n + 1 steps
for (int i = 0; i <= n; i++) {
fast = fast.next;
}
// Step 2: Walk both pointers together
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
// Step 3: Skip target node
slow.next = slow.next.next;
return dummy.next;
}
}
- Time Complexity: (1 pass over the list).
- Space Complexity: .
The key is that for (int i = 0; i <= n; i++) runs times, not times. Counting from up to and including yields total steps.
Because fast takes steps while slow takes steps, the gap between fast and slow becomes nodes.
Why an gap lands slow on the predecessor node
When both pointers move together until fast hits null:
- **1 step behind
null**= Last node ( node from end) - ** steps behind
null**= Target node ( node from end) - ** steps behind
null**= Predecessor node ( node from end)
Step-by-Step Trace
List: dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> null | Remove (Node 4)
1. Create the Step Gap:
- Start:
slow=dummy,fast=dummy i = 0:fastmoves to1i = 1:fastmoves to2i = 2:fastmoves to3
2. Slide both pointers until fast == null:
- Shift 1:
fastat4,slowat1 - Shift 2:
fastat5,slowat2 - Shift 3:
fastatnull,slowat3
fast reached null, and slow stopped at **Node 3**—exactly one node before the target **Node 4**.
Doing steps upfront offsets slow by just enough to land on the node right before the one you need to delete, making slow.next = slow.next.next straightforward.
3 Rules to Remember for Interviews:
- Always use a Dummy Node: Attaching
dummy -> headavoids edge-case checks when deleting the very first node. - Move Fast First by : Creating an gap places
slowdirectly on the predecessor node. - Walk to Null: Move both until
fast == null, then pointslow.nexttoslow.next.next.