Problem overview
The method RemoveRepeats gets a List<int> named nums. Some values may appear more than once. It returns a new list that has each value only once. The first copy of each value stays, later copies are dropped, and the values keep the order in which they first appear.
For [3, 1, 3, 2, 1] the answer is [3, 1, 2]: the second 3 and the second 1 are repeats. For [6, 6, 6, 6] it is [6], and for [4, 9, 2] nothing repeats, so the list comes back unchanged.
Approach
Read the numbers from left to right. For each one, ask: have I met this value before? If not, it is a first copy, so it goes into the result. If so, skip it.
The question "have I met it?" is what a HashSet<int> answers quickly. Keep a set named seen next to the result list. The list holds the answer in the right order; the set holds the same values for fast checking. A set alone would lose the order, and a list alone would have to search itself for every number.
Step by step
- Create an empty
HashSet<int>namedseen. - Create an empty
List<int>namedresult. - Loop over
numswithforeach, in order, so first copies are met before later ones. - Test
!seen.Contains(num). The!turns the answer around: the test is true when the value is new. - Add
numtoresultinside theif. - Add
numtoseenin the sameif, so the next copy is recognised. - Return
resultafter the loop.
The solution in C#
class Solution {
public List<int> RemoveRepeats(List<int> nums) {
HashSet<int> seen = new HashSet<int>();
List<int> result = new List<int>();
foreach (int num in nums) {
if (!seen.Contains(num)) {
result.Add(num);
seen.Add(num);
}
}
return result;
}
}
The details worth knowing:
- A
List<int>grows withAdd; you don't need to know the final size in advance, as you would with an array. - The lesson's tests print the list as values separated by spaces. The method returns the list itself, not text.
- Because
Addon a set returnsfalsefor a repeat, the test and the insertion could be combined:if (seen.Add(num)) { result.Add(num); }. The version above keeps the two steps apart so each is easy to see.
Tracing the first example
| num | new value? | result after |
|---|---|---|
| 3 | yes | [3] |
| 1 | yes | [3, 1] |
| 3 | no | [3, 1] |
| 2 | yes | [3, 1, 2] |
| 1 | no | [3, 1, 2] |
Testing
On the statement's three examples, the method returns:
[3, 1, 2]
[6]
[4, 9, 2]
Edge cases and common mistakes
- Building the answer from a set alone.
new HashSet<int>(nums)drops the repeats, but a set promises no order, so the first-appearance order isn't guaranteed. - Forgetting to add to
seen. Then no value is ever recognised as a repeat, and the result equals the input. - Returning inside the loop. A
returnthere would end the method after the first value. Return after the loop. - Dropping the first copy instead of the later ones. Keeping the last copy gives
[3, 2, 1]for the first example, which is a different answer. - Negative values. Numbers go down to -1,000,000,000, and a set treats them like any other
int. Nothing special is needed.
