Searching & Sorting · 5 of 36
Maximum and minimum of an array using minimum number of comparisons
Find the tallest and the shortest kid in a line by comparing kids two at a time, so you need fewer checks.
- Simple way
- time O(n)
room O(1) - Clever way
- time O(n)
room O(1)
The problem, simply
You get a row of numbers. Give back the smallest one and the biggest one.
That part is easy. The twist is the counting: every time you ask "is this one bigger than that one?" costs one check, a comparison. Find both answers with as few checks as you can. The simple way needs about 2n checks. The clever way needs only about 1.5n.
A story to remember it
Picture it
Six numbers in three pairs: (1000, 11), (445, 1) and (330, 3000). The smaller of each pair is marked checking and only races for the min. The bigger one is marked done and only races for the max.
- checking
- done
Examples
A normal case
- In
[1000, 11, 445, 1, 330, 3000]- Out
min = 1, max = 3000
The pair way uses 7 checks. The simple way uses 10.
An odd count with negatives
- In
[5, -2, 8, 3, -7]- Out
min = -7, max = 8
With an odd count, the first number starts as both the min and the max, and the other four go in pairs. That is 6 checks.
Just one number
- In
[42]- Out
min = 42, max = 42
No checks at all. The only number is both answers.
All the same
- In
[7, 7, 7, 7]- Out
min = 7, max = 7
Ties are fine. Nothing ever gets replaced, and the answer is still right.
Way 1: the simple way
Keep two notes: the smallest so far and the biggest so far. Start both at the first number. Then look at every other number and check it against both notes.
- Set
min = max = arr[0]. - For each next number, check it against
min, then check it againstmax. - At the end, the two notes are the answer.
class Solution {
public int[] findMinMax(int[] arr) {
int min = arr[0], max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) min = arr[i]; // check 1
if (arr[i] > max) max = arr[i]; // check 2
}
return new int[]{min, max};
}
}How long it takes: one walk over the row, O(n), and exactly 2(n - 1) checks. Extra room: two notes, O(1).
You can save a little by writing else if for the max check, because a new minimum can never be a new maximum. That helps on some inputs, but on a row that keeps going up it is still 2(n - 1) checks.
Way 2: the clever way
Take the numbers two at a time.
- If the count is even, compare the first two. The smaller one is
min, the bigger one ismax. Start the pairs at index 2. - If the count is odd, set
min = max = arr[0]. Start the pairs at index 1. - For each pair, compare the two numbers with each other first. Then check only the smaller one against
minand only the bigger one againstmax.
Every pair costs 3 checks instead of 4. A number that lost the "who is bigger" fight can never be the maximum, so there is no reason to check it against max.
Three checks for every pair
01 / 07
Six numbers is an even count, so the first pair sets both notes. Compare 1000 with 11: one check.
- checks
- 1
- checking
- seen
- done
- answer
- just added
class Solution {
public int[] findMinMax(int[] arr) {
int n = arr.length;
int min, max, i;
if (n % 2 == 0) { // even: the first pair sets both notes
if (arr[0] < arr[1]) { min = arr[0]; max = arr[1]; }
else { min = arr[1]; max = arr[0]; }
i = 2;
} else { // odd: the first number is both
min = max = arr[0];
i = 1;
}
for (; i + 1 < n; i += 2) {
int small = arr[i], big = arr[i + 1];
if (small > big) { small = arr[i + 1]; big = arr[i]; } // the pair fight
if (small < min) min = small; // only the small one tries for min
if (big > max) max = big; // only the big one tries for max
}
return new int[]{min, max};
}
}How long it takes: still one walk, O(n), but the number of checks drops to 3n/2 - 2 when n is even and 3(n - 1)/2 when n is odd. Extra room: O(1).
Watch out for
- Starting
minat0ormaxat0. With all negative numbers, a max of0is wrong. Start from a real number in the array. - An empty array has no min or max. Ask the interviewer what to return, or throw an error.
Integer.MAX_VALUEas the starting min works, but it costs an extra check on the first number. Starting from the array is cleaner.
What interviewers ask next
- Can you do better than
3n/2 - 2checks? No. There are2n - 2facts to learn: every number except the min must lose once, and every number except the max must win once. A check between two untouched numbers teaches two facts, but there are onlyn/2such checks, and every other check teaches at most one new fact. - What about splitting the row in half, solving each half, and joining? That divide and conquer way also gives
3n/2 - 2checks whennis a power of 2, but a few more for other sizes, and it usesO(log n)stack room. - Find the second smallest number with few checks. Run a knockout tournament for the min (
n - 1checks). The second smallest must have lost directly to the winner, so check only thoselog nnumbers. - Why does this matter if both ways are
O(n)? When a check is costly, like comparing long strings or asking a slow server, cutting a quarter of the checks is real savings.
Remember this
Grown-up words: a check is a comparison, calling kids up two at a time is the pairwise or tournament method, and the best possible count, ceil(3n/2) - 2, is the lower bound for finding both the minimum and the maximum.