Problem overview
The function distinct_values receives a vector of integers, nums, and returns how many different values it contains. Each value counts once, however many times it appears.
For nums = [1, 5, -3, 1, -4, 2, -4, 7, 7] the different values are 1, 5, -3, -4, 2 and 7, so the answer is 6. An empty vector gives 0.
The lesson asks for O(n log n) time and O(1) extra space. That rules out a second collection for the values already seen, and it points toward sorting.
Approach: sort, then count the changes
The direct way to count distinct values is to compare each element with every earlier one, which is O(n^2). Sorting makes this much easier. Once the vector is in ascending order, equal values sit next to each other in one block. The example becomes:
[-4, -4, -3, 1, 1, 2, 5, 7, 7]
Now an element is a new value exactly when it differs from the element just before it. The first element is always new, so the count starts at 1, and each later position adds 1 when nums[i] != nums[i - 1]. In the sorted example the changes happen at -3, 1, 2, 5 and 7, five of them, and 1 + 5 gives 6.
Sorting costs O(n log n) and the scan is a single pass of O(n), so sorting sets the total.
This works because sorting groups equal values without needing to know them in advance. Sorting by itself never removes or changes anything, so the number of blocks of equal values in the sorted vector is the number of distinct values in the original. The scan only counts where one block ends and the next begins, and it never has to look further than the neighbour on the left.
Step by step
- Handle the empty vector first. The starting code already returns 0 for it. Everything below can assume at least one element exists.
- Sort
numsin place withsort(). The parameter is declaredmut nums, so the function is allowed to reorder it. The call returns nothing. - Start the count at 1. Declare
distinctas a mutablei32, the function's return type. The first element is always a new value. - Loop from the second element with
iin1..nums.len(), so every element has a left neighbour to compare with. - Count each change. If
nums[i]differs fromnums[i - 1], add 1 with+= 1, because Rust has no++operator. - Return
distinctas the last expression, with no semicolon.
The solution in Rust
impl Solution {
fn distinct_values(mut nums: Vec<i32>) -> i32 {
if nums.is_empty() {
return 0;
}
nums.sort();
let mut distinct: i32 = 1;
for i in 1..nums.len() {
if nums[i] != nums[i - 1] {
distinct += 1;
}
}
distinct
}
}
The Rust details worth a closer look:
- The early return matters. The loop reads
nums[i - 1]and the count starts at 1. For an empty vector the count of 1 would be wrong, so the guard returns 0 before the rest runs. 1..nums.len()is safe for any length. A range with start 1 and end 1 is empty, so a one-element vector skips the loop and keeps the count of 1. Becauseistarts at 1,i - 1is never below zero, which matters sinceiis ausize.sort()changes the vector in place. It returns(), so the next line usesnumsitself. The vector was moved into the function, so the caller's copy is not affected.- Integer ordering. The default
sort()ordersi32numerically, so negatives come first. Only equal values need to be adjacent for this algorithm, and any consistent order would give that. - The return type. The count is an
i32because the function says so. The length of a vector is ausize, so a count taken fromnums.len()would need anas i32cast.
Complexity
Time: O(n log n). The sort takes O(n log n). The loop makes one comparison per element, O(n).
Space. The scan uses a few integers. The lesson treats the sort as in-place. Rust's stable sort() may allocate a temporary buffer, so strictly it can use O(n) extra, while sort_unstable() sorts without one and gives the same count here.
Edge cases and common mistakes
- Empty vector. Returns 0 through the guard.
- One element. The loop doesn't run and the answer is 1.
- All values equal. For
[4, 4, 4]no neighbours differ, so the answer is 1. - Counting without sorting. Comparing neighbours in the unsorted vector counts changes between neighbours, not distinct values. In
[1, 2, 1]that would give 3, while the answer is 2. - Starting the count at 0. This misses the first value of every block, and the result is too small by one.
- Counting equal neighbours. Adding 1 when
nums[i] == nums[i - 1]counts the duplicates instead of the distinct values.
Testing
nums = [1, 5, -3, 1, -4, 2, -4, 7, 7]gives 6.nums = []gives 0.nums = [9]gives 1.nums = [4, 4, 4]gives 1.nums = [1, 2, 1]gives 2.
