EasyLinked List

Remove Duplicates from Sorted List โ€” Solution

Problem

Given a sorted linked list, remove all duplicate nodes so that each value appears exactly once and return the deduplicated list.

1
โ†’
1
โ†’
2
โ†’
2
โ†’
3
โ†’โˆ…
[1,1,2,2,3]
  • Input: head = [1, 1, 2, 2, 3]
  • Output: [1, 2, 3]
  • Explanation: Both 1 and 2 appeared twice; after removing duplicates each value appears exactly once.

Intuition

Because the list is already sorted, any duplicate values must be adjacent to each other โ€” you never need to look backward or use extra memory to track what you've seen. A single pointer scan comparing each node to its immediate successor is enough: whenever two neighbors share a value, wire around the second one.

Solution โ€” Single Pass

Walk the list with one pointer. When the current node has the same value as the next, bypass the next node by rewiring current.next to skip it. Only advance the pointer when the values differ โ€” this ensures we collapse runs of any length.

  1. Start current at the head.
  2. Loop while current and current.next both exist.
  3. If current.val == current.next.val, set current.next = current.next.next to skip the duplicate.
  4. Otherwise, advance current = current.next.
  5. Return the original head (unchanged; we modified links in place).
1def deleteDuplicates(self, head: Optional[ListNode]) -> Optional[ListNode]:
2    current = head
3    while current and current.next:
4        if current.val == current.next.val:
5            current.next = current.next.next  # skip the adjacent duplicate
6        else:
7            current = current.next  # only advance when values differ
8    return head

Time: O(n) โ€” each node is visited at most once in the single pass.

Space: O(1) โ€” only one pointer variable; nodes are modified in place with no extra data structures.

Complexity Summary

ApproachTimeSpaceWhen to use
Single PassO(n)O(1)Always โ€” the sorted property makes this directly optimal

Common Mistakes

  • Advancing current after every iteration regardless of a match โ€” when you skip a duplicate, you must not advance; the next node after the rewire may itself be another duplicate with the same value, and advancing would miss it (e.g., 1 โ†’ 1 โ†’ 1 would leave one duplicate behind).
  • Checking current.val == current.val โ€” accidentally comparing the current node to itself (a copy-paste slip) rather than to current.next.val; this condition is always true and produces an infinite loop.
  • Forgetting to guard current.next != null โ€” dereferencing current.next.val without first confirming current.next exists raises a null pointer error on the tail node.
  • Trying to use a hash set โ€” this works but wastes O(n) space and misses the key insight that sorting already eliminates the need for membership tracking.
  • Returning current instead of head โ€” current ends up at the tail after the loop; the caller needs the original head pointer, which is unchanged and must be returned instead.

Related Problems

Ready to practice? Try it on SkillFlow

Adaptive problems, AI follow-up interviews, and a skill score that shows exactly where you need to improve.

Practice This Problem โ†’