array
algorithm
optimization
comparisons
programming

How to find max. and min. in array using minimum comparisons?

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

Finding both the minimum and maximum of an array is easy, but doing it with the fewest comparisons is a classic algorithm question. The naive approach compares every element to both the current minimum and the current maximum, which is simple but not optimal.

The better method processes the array in pairs. By comparing each pair internally first, you reduce the number of comparisons needed against the global minimum and maximum. That gives the optimal linear-time comparison count for this problem.

Why the Naive Approach Uses Too Many Comparisons

The straightforward scan initializes both values from the first element and then compares every remaining element twice:

python
1def min_max_naive(nums):
2    if not nums:
3        raise ValueError("empty input")
4
5    mn = mx = nums[0]
6    comparisons = 0
7
8    for x in nums[1:]:
9        comparisons += 1
10        if x < mn:
11            mn = x
12
13        comparisons += 1
14        if x > mx:
15            mx = x
16
17    return mn, mx, comparisons
18
19
20print(min_max_naive([7, 2, 9, 4, 1, 8, 3]))

This takes 2n - 2 comparisons for an array of length n. It is completely valid and often good enough, but we can do better.

Pairwise Method With Fewer Comparisons

The optimization is to compare items in pairs:

  1. compare the two values in the pair
  2. compare the smaller one to the global minimum
  3. compare the larger one to the global maximum

That is three comparisons for two elements instead of four.

python
1def min_max_pairwise(nums):
2    if not nums:
3        raise ValueError("empty input")
4
5    n = len(nums)
6    comparisons = 0
7
8    if n % 2 == 0:
9        comparisons += 1
10        if nums[0] < nums[1]:
11            mn, mx = nums[0], nums[1]
12        else:
13            mn, mx = nums[1], nums[0]
14        i = 2
15    else:
16        mn = mx = nums[0]
17        i = 1
18
19    while i < n:
20        a, b = nums[i], nums[i + 1]
21
22        comparisons += 1
23        if a < b:
24            local_min, local_max = a, b
25        else:
26            local_min, local_max = b, a
27
28        comparisons += 1
29        if local_min < mn:
30            mn = local_min
31
32        comparisons += 1
33        if local_max > mx:
34            mx = local_max
35
36        i += 2
37
38    return mn, mx, comparisons
39
40
41print(min_max_pairwise([7, 2, 9, 4, 1, 8, 3, 6]))

For even n, this achieves 3n/2 - 2 comparisons. For odd n, it achieves 3(n - 1)/2. That is the standard optimal result for linear comparison-based search of both extrema.

Why the Pairwise Method Is Optimal

The intuition is that every element must somehow participate in the argument that it is not the minimum or not the maximum. Pairing helps because one comparison tells you which element of the pair can only compete for the minimum and which can only compete for the maximum.

After that first comparison:

  • the smaller value never needs to be compared to the global maximum
  • the larger value never needs to be compared to the global minimum

That is exactly where the savings come from.

Divide and Conquer Variant

A divide-and-conquer solution reaches the same asymptotic comparison efficiency:

python
1def min_max_divide(nums):
2    if not nums:
3        raise ValueError("empty input")
4
5    def solve(lo, hi):
6        if lo == hi:
7            return nums[lo], nums[lo], 0
8
9        if hi == lo + 1:
10            if nums[lo] < nums[hi]:
11                return nums[lo], nums[hi], 1
12            return nums[hi], nums[lo], 1
13
14        mid = (lo + hi) // 2
15        left_min, left_max, c1 = solve(lo, mid)
16        right_min, right_max, c2 = solve(mid + 1, hi)
17
18        comparisons = c1 + c2
19        comparisons += 1
20        mn = left_min if left_min < right_min else right_min
21        comparisons += 1
22        mx = left_max if left_max > right_max else right_max
23        return mn, mx, comparisons
24
25    return solve(0, len(nums) - 1)
26
27
28print(min_max_divide([11, 4, 19, 2, 33, 6, 7, 28, 1]))

This version is more mathematical than practical in most everyday code, but it is a useful alternative when recursion already fits the surrounding algorithm.

Common Pitfalls

The most common bug is mishandling odd and even array lengths. The initialization step is different, and getting it wrong causes index errors or skipped values.

Another common problem is quoting the theoretical comparison count without validating the implementation. If you care about the optimization, count comparisons in tests and confirm the result.

People also overvalue this micro-optimization in code where comparisons are cheap and clarity matters more. The naive scan is perfectly acceptable unless the reduced comparison count is actually important.

Finally, always define empty-array behavior explicitly. Returning arbitrary values for empty input is worse than raising a clear error.

Summary

  • The naive one-pass scan uses about 2n - 2 comparisons.
  • The pairwise algorithm reduces this to 3n/2 - 2 for even arrays.
  • Pairing works by deciding local min and max before comparing against the global extrema.
  • A divide-and-conquer solution can achieve the same comparison efficiency.
  • Handle odd and even lengths carefully, and test the implementation instead of trusting the formula alone.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.