Remove Duplicates from Sorted Array
Given an integer array nums sorted in non-decreasing order, remove the duplicates in-place such that each unique element appears only once. The relative order of the elements should be kept the same. Then return the number of unique elements in nums. Consider the number of unique elements of nums to be k. To get accepted, you need to do the following: Change the array nums such that the first k elements of nums contain the unique elements in the order they were present in nums originally. The remaining elements of nums as well as the size of nums do not matter. Return k.
- 1 <= nums.length <= 3 × 10⁴
- -100 <= nums[i] <= 100
- nums is sorted in non-decreasing order
- Solve in-place with O(1) extra memory
nums = [1,1,2]2, nums = [1,2,_]nums = [0,0,1,1,1,2,2,3,3,4]5, nums = [0,1,2,3,4,_,_,_,_,_]nums = [1]1, nums = [1]The array is sorted, and we must keep one copy of each distinct value, packed at the front, in place. We return k, the count of distinct values; the first k slots must hold them in order, and whatever sits after slot k doesn't matter. Because the array is sorted, equal values are forced to sit next to each other — [1, 1, 2, 2, 3] can never scatter its two 1s apart — and that single fact is what makes one forward pass enough.
The tempting move is to delete a value the moment you spot a repeat. But deleting from an array isn't free: to close the hole, every element after it slides one step left. Do that for each duplicate and you're shifting the tail over and over — up to O(N²) work on an array that's mostly repeats. Deletion is the wrong tool.
i = 1
while i < len(nums):
if nums[i] == nums[i-1]:
nums.pop(i) # closing the gap shifts everything after it — slow
else:
i += 1This is the exact read/write shape from Move Zeroes — a reader sweeping the array while a write pointer w trails, marking where the next value we keep belongs. Nothing about the machinery changes. The only thing that changes is the rule for what counts as a "keeper," and here the sorted order hands it to us: keep a value when it differs from the last value we already kept. Since duplicates are guaranteed adjacent, "the last value I kept" is the same as "the value just before this run," so a single comparison decides it — no set of seen values, no extra memory.
The first element is always a keeper (nothing precedes it), so start w at 1 and let the reader begin at index 1. Each time the reader's value differs from nums[w-1] — the most recently kept value — write it at w and advance w. When the sweep ends, w is exactly the count of distinct values, which is what we return.
w = 1 # first element is always kept
for r in range(1, len(nums)):
if nums[r] != nums[w - 1]: # differs from the last value we kept?
nums[w] = nums[r] # keep it at the write position
w += 1
return w # count of distinct valuesnums[w-1], the last kept value, not against nums[r-1], the immediately previous slot. On a sorted array both happen to work, but comparing to the last kept value is the honest expression of the rule ("is this a new distinct value?") and it's the version that survives when you meet trickier variants. Note we never move a keeper backwards, so the surviving values stay in their original sorted order automatically.Same skeleton as Move Zeroes, two slots filled differently: the keeper test became "new distinct value" and the thing we return became w. That's the payoff of learning the pattern rather than the problem — a new keeper rule is often the only thing you write. The linked-list twin of this, Remove Duplicates from Sorted List, is the identical idea with next-pointer rewiring instead of array writes.
One sweep, no deletions, no extra structure: O(N) time, O(1) space, versus the delete-and-shift trap's O(N²). And it all rode on reading the precondition: "sorted" quietly promised that duplicates are neighbours, which is what let a single comparison replace a memory of everything seen. Train the reflex: when an input is sorted, ask what that ordering makes adjacent — adjacency is what lets you swap remembering for a cheap local check.
In-Place De-Duplication
Collector & Scout Pointers
Key Insight
The Slow pointer acts as a gatekeeper, storing unique sorted elements. The Fast pointer scouts ahead. When a new value is found, we extend the unique sequence.
In-Place Efficiency
By modifying the array directly, we achieve O(1) space complexity. We overwrite duplicates with new unique values, so we don't need extra memory.