80. Remove Duplicates from Sorted Array II
80. Remove Duplicates from Sorted Array II
Problem Statement
Given a sorted integer array nums, remove duplicates in-place such that each element appears at most twice, and return the new length.
You must do this with:
- (O(n)) time complexity
- (O(1)) extra space
The relative order of the kept elements should be maintained.
Examples
Example 1:
Input: nums = [1,1,1,2,2,3]
Output: 5
Array becomes: [1,1,2,2,3,_]
Example 2:
Input: nums = [0,0,1,1,1,1,2,3,3]
Output: 7
Array becomes: [0,0,1,1,2,3,3,_,_]
Constraints
1 <= nums.length <= 3 * 10^4-10^4 <= nums[i] <= 10^4numsis sorted in non-decreasing order
Clarification Questions
- In-place: We can overwrite elements in
nums, but cannot use another array? - Return value: We return the new length
k, and LeetCode checks the firstkelements only? - Order: The order of the remaining elements must be kept the same as in the original array?
- Sorted property: The array is always sorted in non-decreasing order (no need to re-sort)?
- Maximum duplicates: Exactly “at most 2” occurrences are allowed for each value (not 1, not 3)?
Interview Deduction Process (20 minutes)
Step 1: Brute-force idea (5 min)
Use a frequency map, then rebuild the array with at most 2 copies of each number.
Problem: uses extra space and is not truly in-place in the intended sense.
Step 2: Two-pointers intuition (7 min)
Because nums is sorted, all duplicates are consecutive.
We can maintain a write pointer k and iterate through the array with a read pointer.
We only write a number if adding it would not violate the “at most two copies” rule.
Step 3: Optimized rule (8 min)
Key trick: instead of counting explicitly, rely on the already-written part of the array.
When deciding whether to write the current number num:
- If
k < 2, always keep (we haven’t even written 2 elements yet). - Otherwise, compare
numwithnums[k - 2].- If
num == nums[k - 2], we already have at least 2 copies — skip. - Otherwise, it is safe to write.
- If
Solution Approach
We use a single pass with a write pointer:
- Let
kbe the index where we write the next valid element. - Iterate through each
numinnums:- If
k < 2, we always keep the element. - If
k >= 2, we only keepnumif it is different fromnums[k - 2].
- If
- After the loop,
kis the new length, and the firstkpositions ofnumshold the answer.
Key Insights
-
Sorted array ⇒ local comparison is enough
Since equal elements are consecutive, to know whether we already kept two copies, we only need to look two positions back. -
No explicit counting
We don’t maintain a frequency counter. The already-written prefix ofnumsimplicitly encodes how many times we have kept each value. -
General pattern
If we want to allow at mostmduplicates, the general condition becomes:
if k < m or nums[k - m] != num: keep it.
Python Solution
Implementation
from typing import List
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
k = 0
for num in nums:
# Keep if we have written fewer than 2 elements so far,
# or if this num is different from the element at k - 2
if k < 2 or nums[k - 2] != num:
nums[k] = num
k += 1
return k
Alternative Index-Based Version
Same idea, but using indices explicitly:
from typing import List
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
n = len(nums)
if n <= 2:
return n
idx = 2 # index to write the next allowed element
for i in range(2, n):
if nums[i] != nums[idx - 2]:
nums[idx] = nums[i]
idx += 1
return idx
Algorithm Explanation
We maintain a prefix nums[:k] that always satisfies the rule:
Every value appears at most twice.
For each new num:
- If
k < 2, the prefix is too short to break the rule, so we write it. - If
k >= 2, then:- If
num == nums[k - 2], we would end up with three identical numbers at indicesk - 2,k - 1,k. We must skip. - If
num != nums[k - 2], we can safely write it atnums[k].
- If
Because the array is sorted, if we see the same value again, it must appear right after the previous occurrences, so this local check is sufficient.
Example Walkthrough
Take nums = [1,1,1,2,2,3].
| Step | num | k (before) | nums[k-2] (if k>=2) | Keep? | nums (prefix shown) |
|---|---|---|---|---|---|
| 1 | 1 | 0 | - | yes | [1] |
| 2 | 1 | 1 | - | yes | [1,1] |
| 3 | 1 | 2 | 1 | no | [1,1] |
| 4 | 2 | 2 | 1 | yes | [1,1,2] |
| 5 | 2 | 3 | 1 | yes | [1,1,2,2] |
| 6 | 3 | 4 | 2 | yes | [1,1,2,2,3] |
Final k = 5, and the array is [1,1,2,2,3, _].
Complexity Analysis
- Time Complexity: (O(n)), where (n) is the length of
nums, since we scan the array once. - Space Complexity: (O(1)), as we only use a few integer variables and mutate the array in-place.
Edge Cases
- Arrays of length
0,1, or2— already valid, just returnlen(nums). - All elements identical, e.g.
[1,1,1,1]⇒[1,1,_,_], return2. - No duplicates at all, e.g.
[1,2,3,4]⇒ unchanged, return4. - Negative values (still sorted), e.g.
[-1,-1,-1,0,0,1].
Common Mistakes
- Counting occurrences explicitly for each value and doing extra passes instead of a single-pass two-pointer solution.
- Comparing only with the previous element (index
k - 1) rather thank - 2, which fails to enforce “at most two” properly. - Using extra arrays or data structures, which breaks the intended in-place constraint.
- Forgetting to handle small arrays (length
< 2) cleanly.
Related Problems
- LC 26: Remove Duplicates from Sorted Array — Allow at most 1 occurrence.
- LC 27: Remove Element — Remove all occurrences of a given value in-place.
- LC 283: Move Zeroes — Another in-place two-pointer array rewrite.