🧠
One Pass Two Pointer (Fast and Slow)
💡 This approach introduces the two-pointer technique with a fixed gap, which is a common pattern in linked list problems and improves efficiency.
Intuition
Use two pointers separated by n nodes. Move both until the fast pointer reaches the end, then the slow pointer is just before the target node.
Algorithm
- Create a dummy node pointing to head to handle edge cases.
- Initialize two pointers, fast and slow, at the dummy node.
- Move fast pointer n+1 steps ahead to maintain a gap, checking for null to avoid errors.
- Move both pointers forward until fast reaches the end.
- Slow pointer now points to the node before the target; remove target node.
- Return dummy.next as the new head.
💡 The key is maintaining the gap so that slow lands exactly before the node to remove in one pass.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def removeNthFromEnd(head: ListNode, n: int) -> ListNode:
dummy = ListNode(0, head)
fast = slow = dummy
for _ in range(n + 1):
if fast is None:
break
fast = fast.next
while fast:
fast = fast.next
slow = slow.next
slow.next = slow.next.next
return dummy.next
# Driver code
if __name__ == '__main__':
head = ListNode(1, ListNode(2, ListNode(3, ListNode(4, ListNode(5)))))
new_head = removeNthFromEnd(head, 2)
curr = new_head
while curr:
print(curr.val, end=' ')
curr = curr.next
print()
Line Notes
dummy = ListNode(0, head)Create dummy node to simplify edge cases like removing the head
fast = slow = dummyInitialize both pointers at dummy to maintain a fixed gap
for _ in range(n + 1):Advance fast pointer n+1 steps to create the gap; check for None to avoid errors
while fast:Move both pointers forward until fast reaches the end, maintaining the gap
class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
public 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++) {
if (fast == null) break;
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
}
public static void main(String[] args) {
ListNode head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
Solution sol = new Solution();
ListNode newHead = sol.removeNthFromEnd(head, 2);
ListNode curr = newHead;
while (curr != null) {
System.out.print(curr.val + " ");
curr = curr.next;
}
System.out.println();
}
}
Line Notes
ListNode dummy = new ListNode(0, head);Create dummy node to handle edge cases like removing head
ListNode fast = dummy, slow = dummy;Initialize both pointers at dummy to maintain the gap
for (int i = 0; i <= n; i++)Advance fast pointer n+1 steps to create the gap; check for null to avoid errors
while (fast != null)Move both pointers forward until fast reaches the end, maintaining the gap
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode dummy(0);
dummy.next = head;
ListNode* fast = &dummy;
ListNode* slow = &dummy;
for (int i = 0; i <= n; i++) {
if (fast == nullptr) break;
fast = fast->next;
}
while (fast) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummy.next;
}
};
int main() {
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
head->next->next->next = new ListNode(4);
head->next->next->next->next = new ListNode(5);
Solution sol;
ListNode* newHead = sol.removeNthFromEnd(head, 2);
ListNode* curr = newHead;
while (curr) {
cout << curr->val << " ";
curr = curr->next;
}
cout << endl;
return 0;
}
Line Notes
ListNode dummy(0);Create dummy node to simplify edge cases like removing head
ListNode* fast = &dummy;Initialize fast pointer at dummy to maintain gap
for (int i = 0; i <= n; i++)Advance fast pointer n+1 steps to create the gap; check for nullptr to avoid errors
while (fast)Move both pointers forward until fast reaches the end, maintaining the gap
function ListNode(val, next = null) {
this.val = val;
this.next = next;
}
var removeNthFromEnd = function(head, n) {
let dummy = new ListNode(0, head);
let fast = dummy, slow = dummy;
for (let i = 0; i <= n; i++) {
if (fast === null) break;
fast = fast.next;
}
while (fast) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummy.next;
};
// Driver code
let head = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(5)))));
let newHead = removeNthFromEnd(head, 2);
let curr = newHead;
let output = [];
while (curr) {
output.push(curr.val);
curr = curr.next;
}
console.log(output.join(' '));
Line Notes
let dummy = new ListNode(0, head);Create dummy node to handle edge cases like removing head
let fast = dummy, slow = dummy;Initialize both pointers at dummy to maintain the gap
for (let i = 0; i <= n; i++)Advance fast pointer n+1 steps to create the gap; check for null to avoid errors
while (fast)Move both pointers forward until fast reaches the end, maintaining the gap
Single pass through the list with two pointers, linear time and constant space.
💡 For n=10^5, this means about 100,000 steps, which is efficient for large inputs.
Interview Verdict: Accepted and optimal
This is the preferred approach in interviews due to its efficiency and elegance.