1. Problem Summary
You are given the head of a sorted singly linked list.
Your task is to remove all nodes that have duplicate values, meaning:
- If a value appears more than once,
- all occurrences of that value must be removed (not just reduced to one).
Return the head of the updated list containing only distinct values.
Example interpretation:
Input:
1 → 2 → 3 → 3 → 4 → 4 → 5
Values 3 and 4 appear more than once, so remove them entirely.
Output:
1 → 2 → 5
2. Key Insights
Sorted list property is crucial
Duplicates will always be adjacent, so detection is easy.
We must remove all nodes with repeated values
This differs from Problem 83, where duplicates are collapsed into one node.
Dummy head simplifies edge cases
If the first values are duplicates, original head must be removed.
Dummy node ensures we always have a stable predecessor pointer.
Two-pointer strategy works best
We use:
prev→ last confirmed unique nodecurrent→ node under inspection
Removal rule
If current.val == current.next.val, advance until value changes, then skip entire block.
3. Approach
Dummy-head traversal with duplicate skipping
Steps:
- Create a dummy node pointing to head
- Initialize:
prev = dummy current = head - While current exists:
- If current has duplicates (current.next exists and both values equal):
- Advance current until value changes (skipping duplicates)
- Link prev.next to current.next (removes entire duplicate block)
- Else (value unique):
- Move prev forward to current
- Move current forward
- If current has duplicates (current.next exists and both values equal):
- Return dummy.next as new head
This ensures only non-duplicate nodes remain.
4. Time and Space Complexity
Time Complexity: O(N)
Each node visited at most twice.
Space Complexity: O(1)
Constant extra pointers, no auxiliary containers.
5. Java Solution
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode prev = dummy;
ListNode current = head;
while (current != null) {
// Detect duplicates
boolean isDuplicate = false;
while (current.next != null && current.val == current.next.val) {
current = current.next;
isDuplicate = true;
}
if (isDuplicate) {
// Skip entire duplicate block
prev.next = current.next;
} else {
prev = prev.next;
}
current = current.next;
}
return dummy.next;
}
}
6. Dry Run Examples
Example 1
Input:
1 → 2 → 3 → 3 → 4 → 4 → 5
Walk-through:
- 1 unique → keep
- 2 unique → keep
- 3 duplicates → skip both
- 4 duplicates → skip both
- 5 unique → keep
Output:
1 → 2 → 5
Example 2
Input:
1 → 1 → 1 → 2 → 3
- 1 repeated → remove all three
- 2 unique
- 3 unique
Output:
2 → 3
Example 3
Input:
1 → 2 → 2 → 2
- 1 unique
- 2 repeated three times → remove
Output:
1
Example 4
Input:
3 → 3 → 3
All duplicates removed
Output:
empty list (null)
Example 5
Input:
1 → 2 → 3 → 4
No duplicates
Output:
1 → 2 → 3 → 4
7. Why This Solution Works
- Sorted order guarantees adjacent duplicates
- Dummy node handles head-removal edge case
- prev pointer ensures safe list rewiring
- Skips full duplicate runs, not individual nodes
- No extra memory or value rewriting needed
8. Common Mistakes
- Treating problem like LC83 and keeping one copy
- Forgetting to remove duplicate blocks completely
- Not handling duplicate sequence at the head
- Advancing prev incorrectly when duplicates found
- Pointer skipping leading to lost nodes or cycles
