Remove Duplicates from Sorted Array
Problem
nums sorted in non-decreasing order. Rearrange
it in-place so that each distinct value appears exactly once at the front, in the
original order, and return that count k. Only
nums[0..k-1] is checked; whatever sits after it does not matter.nums = [1,1,2]Outputk = 2, nums starts with [1,2]nums = [0,0,1,1,1,2,2,3,3,4]Outputk = 5, nums starts with [0,1,2,3,4]1 ≤ n ≤ 3·104 · -100 ≤ nums[i] ≤ 100 · sorted non-decreasing · O(1) extra space
Try it yourself
class Solution {
public int removeDuplicates(int[] nums) {
}
}
Instinct "In place", "O(1) extra space" or "one pass" over an array? Think write pointer, reversal, prefix/suffix passes or using indices as storage.
Memory hack Read fast, write slow, reverse to rotate, and let the index be the hash.
Open the Array Manipulation guide →Approaches
Brute ForceShift Left on Every Duplicate
O(n2)Space O(1)Walk the array; whenever nums[i] equals its predecessor, delete
it by shifting everything after it one slot left and shrinking the logical length.
This is what “remove” means on a plain array.
public int removeDuplicates(int[] nums) { int len = nums.length; // logical length after deletions int i = 1; while (i < len) { if (nums[i] == nums[i - 1]) { // delete nums[i] by shifting the tail left for (int j = i; j < len - 1; j++) { nums[j] = nums[j + 1]; } len--; } else { i++; } } return len; }
In-place, but each deletion is O(n), so many duplicates make it quadratic.
BetterCopy Uniques to a Buffer
O(n)Space O(n)Scan once, appending a value to a temporary array only when it differs
from the previous element. Then copy the buffer's first k values
back into nums.
public int removeDuplicates(int[] nums) { int[] buffer = new int[nums.length]; int k = 0; for (int i = 0; i < nums.length; i++) { if (i == 0 || nums[i] != nums[i - 1]) { buffer[k++] = nums[i]; } } System.arraycopy(buffer, 0, nums, 0, k); return k; }
Linear time, but the buffer breaks the O(1)-space requirement.
OptimalRead/Write Two Pointers
O(n)Space O(1)Use the front of nums as the buffer. write marks
where the next distinct value goes; nums[write-1] is the last kept
value. Since write ≤ read, we never overwrite a value we still
need to read.
public int removeDuplicates(int[] nums) { int write = 1; // nums[0..write-1] holds the distinct values so far for (int read = 1; read < nums.length; read++) { if (nums[read] != nums[write - 1]) { nums[write] = nums[read]; write++; } } return write; }
Dry runnums = [1,1,2]
- r=100r w112330 = last kept, skip
- r=2011r w1233write 1, w=2
- r=40121w2r33r=3 skipped; write 2
- r=501232w3r3write 3, w=4
- r=601232w33skip; k=4 ✔
One pass, in-place — the intended solution.
nums[write-1] (not nums[read-1])
generalises: use nums[write-2] to allow each value twice (LC 80).At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Shift Left on Every Duplicate | O(n2) | O(1) |
| Better | Copy Uniques to a Buffer | O(n) | O(n) |
| Optimal | Read/Write Two Pointers | O(n) | O(1) |
- Starting
writeat 0 —nums[0]is always kept, so the first slot to fill is 1. - Returning
write - 1or the last index instead of the count. - Reaching for a
HashSet— it ignores the sorted order and uses O(n) memory.
- Point out that sortedness makes duplicates adjacent — that's the whole problem.
- State the invariant:
nums[0..write-1]is strictly increasing and complete so far. - Offer the “at most k copies” generalisation to show depth.