Problem Statement
Given an integer array nums
sorted in non-decreasing
order, remove all duplicates in-place so that each unique element appears only once. Return the number of unique elements in the array.
If the number of unique elements be k
, then,
Change the array nums
such that the first k
elements of nums
contain the unique values in the order that they were present originally.
The remaining elements, as well as the size of the array does not matter in terms of correctness.
An array sorted in non-decreasing order is an array where every element to the right of an element in either equal to or greater in value than that element.
LeetCode:
Constraints
1 <= nums.length <= 3 * 10^4
-100 <= nums[i] <= 100
nums is sorted in non-decreasing order.
Examples
Example 1:
Input: arr [1, 2, 5, 5, 6, 6, 7]
Output: arr [1, 2, 5, 6, 7, _, _]
Exaplanation: There are five distinct elements in array and the elements
marked as _ can have any value.
Example 2:
Input: arr [-5, -1, 0, 0, 1, 2]
Output: arr [-5, -1, 0, 1, 2, _]
Example: There are five distinct elements in array and the elements
marked as _ can have any value.
Different Approaches
1️⃣ Brute Force Approach (Using set)
std::set
is an effective approach for removing duplicates from a sorted array. set
only stores unique elements. Copy all the elements back from the set to the original array.
Algorithm:
- Initialize an empty Set.
- iterate through each element of the array and keep pushing in the set.
- Create an empty array.
- Iterate through unique element in the set and keep putting in the original array.
Code:
#include<bits/stdc++.h>
using namespace std;
int removeDuplicates(int arr[], int n) {
// Use a set to store unique elements
set <int> set;
// Insert elements from arr[] into the set
for (int i = 0; i < n; i++) {
set.insert(arr[i]);
}
// Get the number of unique elements
int k = set.size();
int j = 0;
// Copy unique elements from set to nums
for (int x: set) {
arr[j++] = x;
}
// Return the number of unique elements.
return k;
}
int main() {
int arr[] = {1,1,2,2,2,3,3};
int n = sizeof(arr)/sizeof(arr[0]);
int k = removeDuplicates(arr, n);
cout << "The array after removing duplicate elements is " << endl;
for (int i = 0; i < k; i++) {
cout << arr[i] << " ";
}
}
Complexity Analysis:
- Time Complexity:
O(n log n)
- Inserting each element into the set takes
O(log n)
time, and there aren
elements in the array. - Iterating through the set takes
O(n)
time. - Thus, the overall time complexity is dominated by the insertion operation, resulting in
O(n log n)
, if we ignore the dominated operation, it would beO(n log n) + O(n)
- Inserting each element into the set takes
- Space Complexity:
O(n)
- Additional space is required for the set and the vector.
- The size of the set could be at most
n
, wheren
is the number of unique elements in the array. - Therefore, the space complexity is
O(n)
.
2️⃣ Two Pointers Approach
Algorithm:
- Initialization:
- We start by initializing two pointers,
i
, andj
, both pointing to the beginning of the array. - Pointer
i
will keep track of the index where unique elements will be placed. - Pointer
j
will be used to iterate through the array.
- We start by initializing two pointers,
- Iterating through the array:
- We begin iterating through the array with the pointer
j
, starting from the second element. - At each step, we compare the element at index
j
with the element at indexi
. - If the elements are different, it means we have encountered a new unique element.
- In this case, we copy the unique element at index
j
to the positioni + 1
in the array. - We then increment
i
to point to the next position where the next unique element will be placed.
- We begin iterating through the array with the pointer
- Unique Elements:
- As we iterate through the array, each unique element encountered is copied to the position pointed by
i
. - After the iteration completes, all unique elements are compactly placed in the beginning of the array, from index
0
toi
.
- As we iterate through the array, each unique element encountered is copied to the position pointed by
- Removing Duplicates:
- If there are duplicate elements in the array, they will be overwritten as we copy unique elements.
- Returning the Length:
- After the iteration,
i
points to the last unique element in the array. - We return
i + 1
, which represents the length of the array containing only unique elements.
- After the iteration,
Dry Run:
Initialization:
+-----+-----+-----+-----+-----+------+-----+
arr : | 0 | 0 | 1 | 1 | 1 | 2 | 2 |
+-----+-----+-----+-----+-----+------+-----+
0 1 2 3 4 5 6
i j
First Iteration:
--------------------------------------------
arr : | 0 | 0 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
0 != 0
false
hence, increment j by 1
--------------------------------------------
arr : | 0 | 0 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
Second Iteration:
--------------------------------------------
arr : | 0 | 0 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
0 != 1
true
First Increment i, which would point to index 1, arr[1] = 0
set arr[i] = arr[j]
Increment j by 1
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
Third Iteration:
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
1 != 1
false
Increment j by 1
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
Fourth Iteration:
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
1 != 1
false
Increment j by 1
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
Fifth Iteration:
--------------------------------------------
arr : | 0 | 1 | 1 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
1 != 2
true
First Increment i, which would point to index 2, arr[2] = 1
set arr[i] = arr[j]
arr[2] = arr[5]
arr[2] = 2
Increment j by 1
--------------------------------------------
arr : | 0 | 1 | 2 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
Sixth Iteration:
--------------------------------------------
arr : | 0 | 1 | 2 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
arr[i] != arr[j]
2 != 2
false
Increment j by 1
--------------------------------------------
arr : | 0 | 1 | 2 | 1 | 1 | 2 | 2 |
--------------------------------------------
0 1 2 3 4 5 6
i j
j becomes out of bounds so break the loop,
and return i + 1 = number of unique elements.
Code:
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// Edge case: empty array
if (nums.empty()) return 0;
// 'uniqueIndex' keeps track of the last index where a unique element was placed
int uniqueIndex = 0;
// Start from the second element and compare with the last unique one
for (int i = 1; i < nums.size(); i++) {
// If current element is not equal to the last unique element
if (nums[i] != nums[uniqueIndex]) {
// Move the unique index forward
uniqueIndex++;
// Place the new unique element at the new position
nums[uniqueIndex] = nums[i];
}
}
// Number of unique elements is index + 1
return uniqueIndex + 1;
}
};
Complexity Analysis:
- Time Complexity:
O(N)
, for single traversal of the array, where N is the size of the array. - Space Complexity:
O(1)
, not using any extra space.