Remove Duplicates from Sorted List
EasyPrompt
Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well.
Example 1:
Input: head = [1,1,2]
Output: [1,2]Example 2:
Input: head = [1,1,2,3,3]
Output: [1,2,3]
Constraints:
- The number of nodes in the list is in the range
[0, 300]. -100 <= Node.val <= 100- The list is guaranteed to be sorted in ascending order.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves iterating through the linked list and using an auxiliary data structure, a hash set, to keep track of the elements that have already been added to the result list. A new list is constructed containing only the unique elements.
Algorithm
- Initialize an empty
HashSetcalledseen. - Create a
dummynode to act as the starting point for the new, de-duplicated list. - Create a
newTailpointer, initially pointing todummy. - Initialize a
currentpointer to theheadof the input list. - Iterate through the input list with the
currentpointer until it becomesnull. - Inside the loop, check if
current.valexists in theseenset. - If
current.valis not inseen, add it to the set, create a new node with this value, append it to the new list (newTail.next), and movenewTailto this new node. - If
current.valis already inseen, do nothing. - Move
currentto the next node in the original list. - After the loop, return
dummy.next, which is the head of the new list.
Walkthrough
We can solve this problem by creating a new linked list that will only contain the unique elements from the original list.
To do this, we'll use a HashSet to store the values of the nodes we've already encountered. We'll iterate through the original list, and for each node, we check if its value is already in our hash set.
- If the value is not in the set, it's the first time we're seeing this element. We add it to our new list and also add its value to the hash set.
- If the value is already in the set, it's a duplicate, so we simply skip it and move to the next node in the original list.
We use a dummy head node to simplify the process of building the new list.
/** * 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; } * } */import java.util.HashSet;import java.util.Set; class Solution { public ListNode deleteDuplicates(ListNode head) { if (head == null) { return null; } Set<Integer> seen = new HashSet<>(); ListNode dummy = new ListNode(0); // Dummy head for the new list ListNode newTail = dummy; ListNode current = head; while (current != null) { if (!seen.contains(current.val)) { seen.add(current.val); newTail.next = new ListNode(current.val); newTail = newTail.next; } current = current.next; } return dummy.next; }}Complexity
Time
O(N)
Space
O(K), where K is the number of unique elements. In the worst case, this is O(N).
Trade-offs
Pros
Conceptually simple.
Works for unsorted lists as well, making it a more general solution for duplicate removal.
Cons
Uses extra space for the hash set, which is not optimal for this specific problem where the list is sorted.
Requires building a new list, which consumes additional memory and can be slower than an in-place modification.
This is the most efficient approach, which leverages the fact that the input list is sorted. Since it's sorted, any duplicate elements will be adjacent to each other. We can traverse the list and remove duplicates in-place.
Algorithm
- Initialize a pointer
currentto theheadof the list. - Handle the edge case where the list is empty or has only one node by returning
head. - Loop as long as
currentandcurrent.nextare notnull. - Inside the loop, compare
current.valwithcurrent.next.val. - If they are equal, it's a duplicate. Modify the
nextpointer of thecurrentnode to skip the duplicate:current.next = current.next.next. - If they are not equal, the next element is unique. Move the
currentpointer forward:current = current.next. - After the loop terminates, return the original
head, which now points to the de-duplicated list.
Walkthrough
We can solve this problem with a single pass and without using any extra space. The key insight is that because the list is sorted, we can just compare each node with its immediate successor.
We'll use a pointer, let's call it current, that starts at the head of the list. We then look at the next node, current.next.
- If
current.valis the same ascurrent.next.val, we knowcurrent.nextis a duplicate. To remove it, we can simply bypass it by settingcurrent.nexttocurrent.next.next. We don't advancecurrentyet, because the newcurrent.nextcould also be a duplicate (e.g., in a list like1 -> 1 -> 1). - If
current.valis different fromcurrent.next.val, it means there's no duplicate, so we can safely movecurrentone step forward tocurrent.next.
We repeat this process until current or current.next becomes null.
/** * 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 deleteDuplicates(ListNode head) { // Start with the head of the list ListNode current = head; // Traverse the list as long as the current node and the next node are not null while (current != null && current.next != null) { // If the current node's value is the same as the next node's value if (current.val == current.next.val) { // Skip the duplicate node current.next = current.next.next; } else { // Move to the next node current = current.next; } } // Return the modified head of the list return head; }}Complexity
Time
O(N)
Space
O(1)
Trade-offs
Pros
Optimal space complexity of O(1) as it modifies the list in-place.
Optimal time complexity of O(N) with a single pass.
The code is concise and easy to understand.
Cons
This approach only works because the input list is guaranteed to be sorted.
Solutions
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 deleteDuplicates ( ListNode head ) { ListNode cur = head ; while ( cur != null && cur . next != null ) { if ( cur . val == cur . next . val ) { cur . next = cur . next . next ; } else { cur = cur . next ; } } return head ; } }/** * Definition for singly-linked list. * public class ListNode { * public int val; * public ListNode next; * public ListNode(int val=0, ListNode next=null) { * this.val = val; * this.next = next; * } * } */ public class Solution { public ListNode DeleteDuplicates ( ListNode head ) { ListNode cur = head ; while ( cur != null && cur . next != null ) { if ( cur . val == cur . next . val ) { cur . next = cur . next . next ; } else { cur = cur . next ; } } return head ; } }/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */ /** * @param {ListNode} head * @return {ListNode} */ var deleteDuplicates = function ( head ) { let cur = head ; while ( cur && cur . next ) { if ( cur . next . val === cur . val ) { cur . next = cur . next . next ; } else { cur = cur . next ; } } return head ; };/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode() : val(0), next(nullptr) {} * ListNode(int x) : val(x), next(nullptr) {} * ListNode(int x, ListNode *next) : val(x), next(next) {} * }; */ class Solution { public: ListNode * deleteDuplicates ( ListNode * head ) { ListNode * cur = head ; while ( cur != nullptr && cur -> next != nullptr ) { if ( cur -> val == cur -> next -> val ) { cur -> next = cur -> next -> next ; } else { cur = cur -> next ; } } return head ; } };# Definition for singly-linked list. # 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 ]: cur = head while cur and cur . next : if cur . val == cur . next . val : cur . next = cur . next . next else : cur = cur . next return headVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.