Problem#
Given an array of numbers of length N
, find both the minimum and maximum using less than 2 * (N - 2)
comparisons.
Examples#
Example 1#
1
2
3
4
5
6
7
8
Input:
arr = [ 3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]
Output:
min = 1 , max = 9
Explanation:
The minimum element is 1 and maximum element is 9. Using the optimal approach, we can find both with fewer than 2 * ( 8 - 2 ) = 12 comparisons.
Example 2#
1
2
3
4
5
6
7
8
Input:
arr = [ 7 ]
Output:
min = 7 , max = 7
Explanation:
With a single element, no comparisons are needed. Both min and max are the element itself.
Example 3#
1
2
3
4
5
6
7
8
Input:
arr = [ 5 , 2 ]
Output:
min = 2 , max = 5
Explanation:
With two elements, only 1 comparison is needed to determine min and max.
Solution#
Method 1 - Divide and Conquer#
Intuition#
The naive approach would compare each element with both current min and max, requiring 2 comparisons per element (2N comparisons total). We can optimize this by using a divide and conquer approach or by pairing elements and comparing pairs first, then comparing winners and losers separately.
Approach#
If array has 1 element, return it as both min and max (0 comparisons)
If array has 2 elements, compare them once and return (1 comparison)
For larger arrays, recursively divide into two halves
Find min and max in each half recursively
Combine results by comparing the mins and maxs from both halves (2 comparisons)
This approach uses approximately 3N/2 comparisons, which is less than 2*(N-2) for N > 2
Code#
Cpp
Go
Java
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public :
pair< int , int > findMinMax(vector< int >& arr, int left, int right) {
if (left == right) {
return {arr[left], arr[left]};
}
if (right - left == 1 ) {
if (arr[left] < arr[right]) {
return {arr[left], arr[right]};
} else {
return {arr[right], arr[left]};
}
}
int mid = left + (right - left) / 2 ;
auto leftResult = findMinMax(arr, left, mid);
auto rightResult = findMinMax(arr, mid + 1 , right);
int minVal = min(leftResult.first, rightResult.first);
int maxVal = max(leftResult.second, rightResult.second);
return {minVal, maxVal};
}
pair< int , int > findMinMax(vector< int >& arr) {
if (arr.empty()) return {0 , 0 };
return findMinMax (arr, 0 , arr.size() - 1 );
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func findMinMax (arr []int ) (int , int ) {
if len(arr ) == 0 {
return 0 , 0
}
var helper func ([]int , int , int ) (int , int )
helper = func (arr []int , left , right int ) (int , int ) {
if left == right {
return arr [left ], arr [left ]
}
if right - left == 1 {
if arr [left ] < arr [right ] {
return arr [left ], arr [right ]
}
return arr [right ], arr [left ]
}
mid := left + (right - left )/ 2
leftMin , leftMax := helper (arr , left , mid )
rightMin , rightMax := helper (arr , mid + 1 , right )
minVal := leftMin
if rightMin < minVal {
minVal = rightMin
}
maxVal := leftMax
if rightMax > maxVal {
maxVal = rightMax
}
return minVal , maxVal
}
return helper (arr , 0 , len(arr )- 1 )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public int [] findMinMax (int [] arr) {
if (arr.length == 0) return new int [] {0, 0};
return findMinMax(arr, 0, arr.length - 1);
}
private int [] findMinMax (int [] arr, int left, int right) {
if (left == right) {
return new int [] {arr[ left] , arr[ left] };
}
if (right - left == 1) {
if (arr[ left] < arr[ right] ) {
return new int [] {arr[ left] , arr[ right] };
} else {
return new int [] {arr[ right] , arr[ left] };
}
}
int mid = left + (right - left) / 2;
int [] leftResult = findMinMax(arr, left, mid);
int [] rightResult = findMinMax(arr, mid + 1, right);
int minVal = Math.min (leftResult[ 0] , rightResult[ 0] );
int maxVal = Math.max (leftResult[ 1] , rightResult[ 1] );
return new int [] {minVal, maxVal};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution :
def findMinMax (self, arr: List[int]) -> Tuple[int, int]:
if not arr:
return (0 , 0 )
return self. _findMinMax(arr, 0 , len(arr) - 1 )
def _findMinMax (self, arr: List[int], left: int, right: int) -> Tuple[int, int]:
if left == right:
return (arr[left], arr[left])
if right - left == 1 :
if arr[left] < arr[right]:
return (arr[left], arr[right])
else :
return (arr[right], arr[left])
mid = left + (right - left) // 2
left_min, left_max = self. _findMinMax(arr, left, mid)
right_min, right_max = self. _findMinMax(arr, mid + 1 , right)
min_val = min(left_min, right_min)
max_val = max(left_max, right_max)
return (min_val, max_val)
Complexity#
⏰ Time complexity: O(n)
, where n is the length of the array. We visit each element exactly once
🧺 Space complexity: O(log n)
, due to the recursion stack depth in the divide and conquer approach
Method 2 - Pairwise Comparison#
Intuition#
Instead of comparing each element with both min and max separately, we can pair up elements and compare them first. For each pair, we compare the smaller element with current min and larger element with current max. This reduces the total number of comparisons.
Approach#
Handle edge cases: empty array, single element, two elements
Initialize min and max using the first two elements (1 comparison)
Process remaining elements in pairs:
Compare the two elements in each pair (1 comparison)
Compare the smaller with current min (1 comparison if needed)
Compare the larger with current max (1 comparison if needed)
If array length is odd, handle the last unpaired element
Total comparisons: 3⌊n/2⌋ which is approximately 1.5n, well under 2*(n-2)
Code#
Cpp
Go
Java
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Solution {
public :
pair< int , int > findMinMax(vector< int >& arr) {
int n = arr.size();
if (n == 0 ) return {0 , 0 };
if (n == 1 ) return {arr[0 ], arr[0 ]};
int minVal, maxVal;
int i;
if (arr[0 ] < arr[1 ]) {
minVal = arr[0 ];
maxVal = arr[1 ];
} else {
minVal = arr[1 ];
maxVal = arr[0 ];
}
for (i = 2 ; i < n - 1 ; i += 2 ) {
int smaller, larger;
if (arr[i] < arr[i + 1 ]) {
smaller = arr[i];
larger = arr[i + 1 ];
} else {
smaller = arr[i + 1 ];
larger = arr[i];
}
if (smaller < minVal) minVal = smaller;
if (larger > maxVal) maxVal = larger;
}
if (i == n - 1 ) {
if (arr[i] < minVal) minVal = arr[i];
if (arr[i] > maxVal) maxVal = arr[i];
}
return {minVal, maxVal};
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
func findMinMax (arr []int ) (int , int ) {
n := len(arr )
if n == 0 {
return 0 , 0
}
if n == 1 {
return arr [0 ], arr [0 ]
}
var minVal , maxVal int
if arr [0 ] < arr [1 ] {
minVal , maxVal = arr [0 ], arr [1 ]
} else {
minVal , maxVal = arr [1 ], arr [0 ]
}
i := 2
for i < n - 1 {
var smaller , larger int
if arr [i ] < arr [i + 1 ] {
smaller , larger = arr [i ], arr [i + 1 ]
} else {
smaller , larger = arr [i + 1 ], arr [i ]
}
if smaller < minVal {
minVal = smaller
}
if larger > maxVal {
maxVal = larger
}
i += 2
}
if i == n - 1 {
if arr [i ] < minVal {
minVal = arr [i ]
}
if arr [i ] > maxVal {
maxVal = arr [i ]
}
}
return minVal , maxVal
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Solution {
public int [] findMinMax (int [] arr) {
int n = arr.length ;
if (n == 0) return new int [] {0, 0};
if (n == 1) return new int [] {arr[ 0] , arr[ 0] };
int minVal, maxVal;
if (arr[ 0] < arr[ 1] ) {
minVal = arr[ 0] ;
maxVal = arr[ 1] ;
} else {
minVal = arr[ 1] ;
maxVal = arr[ 0] ;
}
int i;
for (i = 2; i < n - 1; i += 2) {
int smaller, larger;
if (arr[ i] < arr[ i + 1] ) {
smaller = arr[ i] ;
larger = arr[ i + 1] ;
} else {
smaller = arr[ i + 1] ;
larger = arr[ i] ;
}
if (smaller < minVal) minVal = smaller;
if (larger > maxVal) maxVal = larger;
}
if (i == n - 1) {
if (arr[ i] < minVal) minVal = arr[ i] ;
if (arr[ i] > maxVal) maxVal = arr[ i] ;
}
return new int [] {minVal, maxVal};
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Solution :
def findMinMax (self, arr: List[int]) -> Tuple[int, int]:
n = len(arr)
if n == 0 :
return (0 , 0 )
if n == 1 :
return (arr[0 ], arr[0 ])
if arr[0 ] < arr[1 ]:
min_val, max_val = arr[0 ], arr[1 ]
else :
min_val, max_val = arr[1 ], arr[0 ]
i = 2
while i < n - 1 :
if arr[i] < arr[i + 1 ]:
smaller, larger = arr[i], arr[i + 1 ]
else :
smaller, larger = arr[i + 1 ], arr[i]
if smaller < min_val:
min_val = smaller
if larger > max_val:
max_val = larger
i += 2
if i == n - 1 :
if arr[i] < min_val:
min_val = arr[i]
if arr[i] > max_val:
max_val = arr[i]
return (min_val, max_val)
Complexity#
⏰ Time complexity: O(n)
, where n is the length of the array. We process each element exactly once
🧺 Space complexity: O(1)
, only using constant extra space for variables
Analysis#
Both methods achieve the goal of using fewer than 2 * (N - 2)
comparisons:
Method 1 (Divide and Conquer) : Uses approximately 1.5n
comparisons
Method 2 (Pairwise) : Uses exactly 3⌊n/2⌋
comparisons
For the constraint 2 * (N - 2)
, when N ≥ 3:
Required: < 2N - 4
comparisons
Method 1: ≈ 1.5N
comparisons
Method 2: ≈ 1.5N
comparisons
Both methods significantly outperform the naive approach of 2N
comparisons and meet the optimization requirement.