Problem overview
The function min_and_max receives a vector of integers, nums, which always holds at least one number. It returns the smallest and the largest number together as a tuple (i32, i32), smallest first. The lesson asks for one loop and no built-in min or max.
For [4, -2, 9, 0] the answer is (-2, 9). For [7] it is (7, 7), because the only number is both the smallest and the largest. For [-5, -1, -8] it is (-8, -1).
Approach
Keep two running answers, low and high, and compare every number with both. After one pass, low holds the smallest number seen and high the largest.
The starting values matter. Starting both at 0 would be wrong when every number is above 0 (the low would stay 0) or every number is below 0 (the high would stay 0). The first number is always a real candidate, so both start at nums[0]. That is safe because the vector is never empty; on an empty vector, nums[0] would panic.
The two answers are independent. A number can change low, change high, or change neither, and the first number cannot change either, because it already is both.
Step by step
- Declare
lowandhighwithlet mut, both set tonums[0]. They needmutbecause the loop will change them. - Loop over
&nums. Borrowing the vector leaves it usable after the loop. The loop variablenumis a reference to a number. - Update
low. If*numis smaller thanlow, store it. The*reads the number that the reference points to. - Update
high. If*numis larger thanhigh, store it. This is a separateif, so each number is checked against both running answers. - Return
(low, high)after the loop, smallest first.
The solution in Rust
impl Solution {
fn min_and_max(nums: Vec<i32>) -> (i32, i32) {
let mut low = nums[0];
let mut high = nums[0];
for num in &nums {
if *num < low {
low = *num;
}
if *num > high {
high = *num;
}
}
return (low, high);
}
}
The function returns a tuple, which is how a Rust function hands back two values at once. Comparing *num with low works because both are i32; writing num < low would compare a reference with a number, and the compiler reports mismatched types.
The loop reads each number once, so the time grows in step with the length of the vector, and the function stores just two extra numbers however long the vector is. Rust's iterators also have ready-made min() and max() methods, but this lesson asks you to write the comparison yourself, which shows what those methods do inside.
Following the loop
For nums = [4, -2, 9, 0], these are the values of low and high after each number is checked:
| num | low | high |
|---|---|---|
| 4 | 4 | 4 |
| -2 | -2 | 4 |
| 9 | -2 | 9 |
| 0 | -2 | 9 |
Sorting the vector and taking its first and last numbers would also work, but it does more work than this single pass needs.
Testing
Running the function on the three examples prints:
(-2, 9)
(7, 7)
(-8, -1)
Edge cases and common mistakes
- Starting at 0. It fails on all-positive and all-negative vectors, as in the third example.
- Forgetting the
*.numis a reference, so comparing or storing it without*gives a type error. - One number. Both answers stay at that number, which is why a single value gives
(7, 7).
