Problem overview
minAndMax receives a vector of integers, nums, with at least one number. It returns a pair<int, int>: the smallest number first, the largest second. Both must be found in one loop, without a built-in min or max function.
For [4, -2, 9, 0] the result 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: two variables, one pass
Keep two numbers while looping: the smallest seen so far, low, and the largest seen so far, high. Each new number is compared with both, and replaces whichever it beats. After the last number, the two variables hold the answer, and the function returns them as a pair.
The starting values matter. If low started at 0, a vector such as [5, 3, 9] would report a smallest number of 0, which is not in the vector. If high started at 0, an all-negative vector would break the same way. Starting both at nums[0] is safe, because the first number is always a real candidate for both roles. The constraints promise at least one number, so nums[0] exists.
Step by step
- Declare
lowandhighasints, both set tonums[0]. - Loop over
numswith a range-basedfor, taking each value as anintnamednum. - Check for a new minimum: if
num < low, setlow = num. - Check for a new maximum in a separate
if: ifnum > high, sethigh = num. The two checks are independent, so they are twoifs, not anelse. - Return
{low, high}. Curly braces build the pair the function's return type asks for.
Tracing the first example
On [4, -2, 9, 0], both variables start at 4. After each number the loop leaves these values:
num=4 low=4 high=4
num=-2 low=-2 high=4
num=9 low=-2 high=9
num=0 low=-2 high=9
The first number changes nothing, because it is compared with itself. The -2 beats low, the 9 beats high, and the 0 is between them, so it changes neither. The function returns {-2, 9}. Each number is looked at once, so the work grows in step with the length of the vector, and only two extra integers are stored.
This lesson asks for the loop on purpose: it shows how a maximum or a minimum is built from one comparison repeated. Later, the library functions min_element and max_element do the same job, but each needs its own pass over the vector, and the point here is to do both in one.
The solution in C++
class Solution {
public:
pair<int, int> minAndMax(vector<int>& nums) {
int low = nums[0];
int high = nums[0];
for (int num : nums) {
if (num < low) {
low = num;
}
if (num > high) {
high = num;
}
}
return {low, high};
}
};
The loop starts again at the first number, which is compared with itself and changes nothing. That keeps the loop simple. The caller reads the result with .first and .second, or unpacks it with auto [low, high] = ....
Edge cases and common mistakes
- Starting at 0. With
int low = 0;the vector{5, 3, 9}gives a smallest value of 0, which isn't in it. Start fromnums[0]. - One number.
{7}returns(7, 7): neitherifever fires. - All negative.
{-5, -1, -8}returns(-8, -1). Ahighstarting at 0 would wrongly stay 0. - Using
else iffor the second check. Each number must be tested against both variables. Joining the checks withelseonly works by luck on some inputs; keep two separateifs. - Swapped order. The pair is (smallest, largest).
return {high, low};compiles and gives the wrong answer.
Reading the result
The returned pair can be used in two ways. With members, auto result = minAndMax(nums); gives result.first for the smallest and result.second for the largest. With unpacking, auto [smallest, largest] = minAndMax(nums); gives each value its own name. Both read the same pair, and the order is always smallest first.
Other ways, and why this one
You could sort a copy of the vector and take its first and last elements. That also works, but sorting does far more work than the problem needs, and it needs a copy so that the caller's vector keeps its order. You could also loop twice, once for each value. That is correct as well, but the lesson asks for one pass, which reads each number once and keeps both answers up to date as it goes.
Returning a pair is a good fit because the two values always travel together and have a fixed order. The function's return type, pair<int, int>, tells the caller that two integers come back, and the curly braces in return {low, high}; build that pair without naming its type again.
The constraints keep every number between -1,000,000 and 1,000,000, so an int holds them with room to spare, and comparing them never overflows. Nothing is added or multiplied here, so size is not a worry at all.
Testing
[4, -2, 9, 0]gives-2 9.[7]gives7 7.[-5, -1, -8]gives-8 -1.
