Problem overview
The function distinctValues receives a slice of integers, nums, and returns how many different values it contains. For [1, 5, -3, 1, -4, 2, -4, 7, 7] the distinct values are 1, 5, -3, -4, 2 and 7, so the answer is 6. The statement asks for O(n) time and O(n) extra space.
Approach: remember what you have seen
One way to count distinct values is to sort the slice and count the places where a value differs from its neighbour. That costs O(n log n) for the sort. To reach O(n), trade memory for speed: keep a set of the values met so far. For each number, a set lookup takes O(1) on average. If the number is new, add it and count it; if it is already there, ignore it. Each distinct value is counted exactly once, on its first appearance.
Go has no set type, so a map stands in for one: map[int]struct{}. Only the keys matter. The empty struct struct{} has no fields and takes no memory, which makes it the conventional "no value" marker.
On the example, the first 1, 5, -3, -4, 2 and 7 are new and bump the counter. The second 1, the second -4 and the second 7 are found in the set and skipped. The counter ends at 6.
The sorting variant needs no extra memory beyond the sort itself, but it reorders the input and is slower for large slices. The set variant leaves nums untouched and finishes in a single pass, at the cost of up to n stored keys. That trade of space for time is the central idea of the hash table chapter.
Step by step
- Create the set
valueswithmake(map[int]struct{}). - Start the counter
answerat 0. - Loop over
numswithrange, naming each elementvalueand discarding the index with_. The order does not matter for counting. - Test membership with the comma-ok lookup in the
ifheader and check!exists. Only a first-time value passes. - Record it and count it: store
struct{}{}under the key and incrementanswer. - Return
answer.
The solution in Go
func distinctValues(nums []int) int {
values := make(map[int]struct{})
answer := 0
for _, value := range nums {
if _, exists := values[value]; !exists {
values[value] = struct{}{}
answer++
}
}
return answer
}
The Go details:
struct{}{}. The firststruct{}is the type, the second pair of braces creates a value of it.- Comma-ok. Reading a missing key gives the zero value, and for a struct with no fields that tells you nothing. The second result,
exists, is what reports presence. It is scoped to theif. - The counter and the map. The answer could also be read as
len(values)after the loop. The explicit counter makes the "first time seen" logic visible.
Complexity
Time: O(n). One pass, with one lookup and at most one insertion per element, each O(1) on average.
Space: O(n). In the worst case, every value is distinct and the map holds n keys.
Edge cases and common mistakes
- Empty or nil slice. The loop does not run and the result is 0.
- All equal values.
[4, 4, 4]gives 1. - Zero as a value.
[0]gives 1. With a plain read such asvalues[0]a stored zero and a missing key would look alike, which is why the code uses comma-ok. - Counting every element. Incrementing outside the
ifwould just returnlen(nums). - Writing to a nil map. Declaring
var values map[int]struct{}withoutmakewould panic on the first insertion.
Testing
[1, 5, -3, 1, -4, 2, -4, 7, 7]gives6.[]gives0.[4, 4, 4]gives1.[0]gives1.