Imagine you are looking at a line of numbered boxcars on a train track. Your job is to make sure every boxcar has a unique number. Normally, finding duplicates would require checking the entire train multiple times.
However, there is a massive advantage given to us in this problem: the linked list is already sorted.
Because the list is sorted in ascending order, any duplicate values are guaranteed to be sitting right next to each other.
head = [1, 1, 2] -> Output: [1, 2]head = [1, 1, 2, 3, 3] -> Output: [1, 2, 3]The challenge is to iterate through this list and sever the ties to any redundant nodes, effectively cutting them out of the chain.
Walk through the logic step-by-step using the interactive visualizer below. Watch how the pointer successfully skips over redundant nodes by simply rerouting the arrow.
The optimal approach is remarkably straightforward. Because we know duplicates are adjacent, we only need a single pointer (curr) to traverse the list.
Here is the real-world analogy:
curr).curr.next).curr.next = curr.next.next). You stay exactly where you are to check if the new car you just hooked up is also a duplicate!curr = curr.next) and repeat the process.This process continues until you either run out of cars or the car you are standing on is the last one in the line.
Here is the production-ready code in three popular languages. Notice how short and elegant the logic is.
class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = (val===undefined ? 0 : val);
this.next = (next===undefined ? null : next);
}
}
function deleteDuplicates(head: ListNode | null): ListNode | null {
let curr = head;
while (curr !== null && curr.next !== null) {
if (curr.val === curr.next.val) {
// Duplicate found: bypass the next node
curr.next = curr.next.next;
} else {
// Values are distinct: move pointer forward
curr = curr.next;
}
}
return head;
}class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
class Solution:
def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
curr = head
while curr and curr.next:
if curr.val == curr.next.val:
# Bypass the duplicate node
curr.next = curr.next.next
else:
# Move to the next unique node
curr = curr.next
return headpublic 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 deleteDuplicates(ListNode head) {
ListNode curr = head;
while (curr != null && curr.next != null) {
if (curr.val == curr.next.val) {
// Skip over the duplicate node
curr.next = curr.next.next;
} else {
// Advance the pointer
curr = curr.next;
}
}
return head;
}
}curr pointer forward. Therefore, the time taken scales linearly directly with the number of nodes (N) in the list.curr pointer, which means our extra memory usage remains strictly constant regardless of the size of the input list.curr.next = curr.next.next), do not advance your curr pointer immediately. The newly attached node might also be a duplicate of your current node (e.g., [3, 3, 3]).curr and curr.next are not null before trying to read their .val properties to prevent runtime crashes.Stay sharp for coding assessments and final exams! Use our free academic tools, including the GPA to Percentage Calculator, to check your academic stats and keep your records in order.
Found this article helpful? Share it with others!
More articles you might find helpful
Learn the optimal O(n) time and O(1) space solution for LeetCode 234: Palindrome Linked List using the Two-Pointer technique.
Master LeetCode 203: Remove Linked List Elements. Learn how the Dummy Node technique effortlessly handles edge cases with our interactive visualizer and code guides.
Master the LeetCode 837 New 21 Game using dynamic programming and a sliding window approach with interactive visualizers.
Use our free AnaCGPA Calculator for instant and accurate grade calculation. Simple, fast, and no sign-up required.