Remove Duplicates from Sorted Array
EasyPrompt
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 things:
- Change the array
numssuch that the firstkelements ofnumscontain the unique elements in the order they were present innumsinitially. The remaining elements ofnumsare not important as well as the size ofnums. - Return
k.
Custom Judge:
The judge will test your solution with the following code:
int[] nums = [...]; // Input array
int[] expectedNums = [...]; // The expected answer with correct length
int k = removeDuplicates(nums); // Calls your implementation
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
assert nums[i] == expectedNums[i];
}If all assertions pass, then your solution will be accepted.
Example 1:
Input: nums = [1,1,2]
Output: 2, nums = [1,2,_]
Explanation: Your function should return k = 2, with the first two elements of nums being 1 and 2 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).Example 2:
Input: nums = [0,0,1,1,1,2,2,3,3,4]
Output: 5, nums = [0,1,2,3,4,_,_,_,_,_]
Explanation: Your function should return k = 5, with the first five elements of nums being 0, 1, 2, 3, and 4 respectively.
It does not matter what you leave beyond the returned k (hence they are underscores).
Constraints:
1 <= nums.length <= 3 * 104-100 <= nums[i] <= 100numsis sorted in non-decreasing order.
Approaches
2 approaches with complexity analysis and trade-offs.
A straightforward approach is to use an auxiliary data structure, like another array or a list, to store only the unique elements. We can iterate through the original sorted array and add an element to our auxiliary array only if it's different from the last element we added. After processing all elements, we copy the contents of the auxiliary array back into the beginning of the original array and return its size.
Algorithm
- Check for the edge case where the input array
numsis empty. If so, return 0. - Create a new
ArrayList<Integer>calleduniqueElements. - Add the first element of
nums(nums[0]) touniqueElements. - Iterate through the
numsarray from the second element (i = 1tonums.length - 1). - For each element
nums[i], compare it with the previous elementnums[i-1]. - If
nums[i]is not equal tonums[i-1], it's a new unique element, so add it to theuniqueElementslist. - After the loop finishes, the
uniqueElementslist contains all the unique items in the correct order. - Copy the elements from
uniqueElementsback to the originalnumsarray. Iterate fromj = 0touniqueElements.size() - 1and setnums[j] = uniqueElements.get(j). - Finally, return the size of the
uniqueElementslist, which is the count of unique elementsk.
Walkthrough
This method prioritizes simplicity over space efficiency. It doesn't modify the array in-place directly during the identification of unique elements, but rather uses an intermediate storage.
import java.util.ArrayList;import java.util.List; class Solution { public int removeDuplicates(int[] nums) { if (nums.length == 0) { return 0; } List<Integer> uniqueElements = new ArrayList<>(); uniqueElements.add(nums[0]); for (int i = 1; i < nums.length; i++) { if (nums[i] != nums[i-1]) { uniqueElements.add(nums[i]); } } for (int i = 0; i < uniqueElements.size(); i++) { nums[i] = uniqueElements.get(i); } return uniqueElements.size(); }}Complexity
Time
O(N)
Space
O(k)
Trade-offs
Pros
Conceptually simple and easy to follow.
Separates the logic of finding unique elements from modifying the original array.
Cons
Requires extra space proportional to the number of unique elements, which can be up to O(N) in the worst case (all elements are unique).
Violates the strict 'in-place' O(1) space complexity constraint of the problem.
The optimal solution uses a two-pointer technique to solve the problem in-place, adhering to the O(1) space complexity constraint. We use a 'slow' pointer to track the position for the next unique element and a 'fast' pointer to scan the array. Since the array is sorted, all duplicate elements will be grouped together. We can overwrite the duplicates with the next unique element found by the fast pointer.
Algorithm
- Handle the edge case: if the array has 0 or 1 elements, no duplicates can exist, so return its length.
- Initialize a slow pointer
insertIndexto 1. The element at index 0 is considered the first unique element and is already in its correct place. - Initialize a fast pointer
ito 1. - Iterate with the fast pointer
ifrom 1 to the end of the array (nums.length - 1). - In each iteration, compare the current element
nums[i]with the previous elementnums[i-1]. - If
nums[i]is different fromnums[i-1], it means we've found a new unique element. - Place this unique element at the position indicated by the slow pointer:
nums[insertIndex] = nums[i]. - Increment the slow pointer
insertIndexto mark the new end of the unique elements subarray. - If
nums[i]is the same asnums[i-1], it's a duplicate. We do nothing but let the fast pointeriadvance, effectively skipping the duplicate. - After the loop completes,
insertIndexwill be the count of unique elements,k. The firstkelements ofnumswill be the unique elements. ReturninsertIndex.
Walkthrough
This approach modifies the array in-place. The slow pointer, let's call it insertIndex, effectively divides the array into two parts: the processed part with unique elements [0...insertIndex-1] and the unprocessed part [insertIndex...n-1]. The fast pointer iterates through the entire array to find elements to be placed in the processed part.
class Solution { public int removeDuplicates(int[] nums) { if (nums.length == 0) { return 0; } // insertIndex is the slow-runner pointer, indicating the next position for a unique element. int insertIndex = 1; // i is the fast-runner pointer that scans the array. for (int i = 1; i < nums.length; i++) { // If the current element is different from the previous one, it's a new unique element. if (nums[i] != nums[i-1]) { // Place it at the next available position for unique elements. nums[insertIndex] = nums[i]; // Increment the count of unique elements found so far. insertIndex++; } } return insertIndex; }}Complexity
Time
O(N)
Space
O(1)
Trade-offs
Pros
Extremely efficient in terms of space, using O(1) extra space.
Solves the problem in a single pass, resulting in an optimal time complexity of O(N).
Directly modifies the array in-place as required.
Cons
The in-place modification can be slightly harder to reason about for beginners compared to using an auxiliary array.
Solutions
Solution
public class Solution { public int RemoveDuplicates ( int [] nums ) { int k = 0 ; foreach ( int x in nums ) { if ( k == 0 || x != nums [ k - 1 ]) { nums [ k ++] = x ; } } return k ; } }class Solution {public int removeDuplicates(int[] nums) { int k = 0; for (int x : nums) { if (k == 0 || x != nums[k - 1]) { nums[k++] = x; } } return k; }}/** * @param {number[]} nums * @return {number} */ var removeDuplicates = function (nums) { let k = 0; for (const x of nums) { if (k === 0 || x !== nums[k - 1]) { nums[k++] = x; } } return k; };class Solution {public: int removeDuplicates(vector<int> &nums) { nums.erase(unique(nums.begin(), nums.end()), nums.end()); return nums.size(); }};class Solution: def removeDuplicates(self, nums: List[int]) -> int: k = 0 for x in nums: if k == 0 or x != nums[k - 1]: nums[k] = x k += 1 return kVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.