Given an array of integers find the maximum and minimum elements by using minimum comparisons.
Naive Solution
We can simply iterate through the array and compare twice to each element to get min and max. This leads to 2n comparisons.
//sudo code max = A[0], min = A[0] for each i in A if A[i]>max then max = A[i] if A[i]<min then min = A[i] |
public static void minmax0(int[] a) { if (a == null || a.length < 1) return; int min = a[0]; int max = a[0]; for (int i = 1; i <= a.length - 1; i++) { if (max < a[i]) { max = a[i]; } if (min > a[i]) { min = a[i]; } } System.out.println("min: " + min + "\nmax: " + max); } |
# of comparisons: 2(n-1).
Apparently, we should do better than this.
Better Solution 1
public static void minmax1(int[] a) { if (a == null || a.length < 1) return; int min, max; // if only one element if (a.length == 1) { max = a[0]; min = a[0]; System.out.println("min: " + min + "\nmax: " + max); return; } if (a[0] > a[1]) { max = a[0]; min = a[1]; } else { max = a[1]; min = a[0]; } for (int i = 2; i <= a.length - 1; i++) { if (max < a[i]) { max = a[i]; } else if (min > a[i]) { min = a[i]; } } System.out.println("min: " + min + "\nmax: " + max); } |
# of comparisons in worst case: 1 + 2(n-2) = 2n -1
# of comparisons in best case: 1 + (n–2) = n -1
# of comparisons on average: 1.5n -1
In the above implementation, worst case occurs when elements are sorted in descending order, because every time max is greater than a[i] and the second condition always gets executed. The best case occurs when elements are sorted in ascending order, because every time max is less than a[i] and the second condition never gets executed.
Better Solution 2
The number of comparisons can be reduced by comparing pairs first:
public static void minmax2(int[] a) { if (a == null || a.length < 1) return; int min, max; // if only one element if (a.length == 1) { max = a[0]; min = a[0]; System.out.println("min: " + min + "\nmax: " + max); return; } if (a[0] > a[1]) { max = a[0]; min = a[1]; } else { max = a[1]; min = a[0]; } for (int i = 2; i <= a.length - 2;) { if (a[i] > a[i + 1]) { min = Math.min(min, a[i + 1]); max = Math.max(max, a[i]); } else { min = Math.min(min, a[i]); max = Math.max(max, a[i + 1]); } i = i + 2; } if (a.length % 2 == 1) { min = Math.min(min, a[a.length - 1]); max = Math.max(max, a[a.length - 1]); } System.out.println("min: " + min + "\nmax: " + max); } |
# of comparisons when n is even: 1 + 3 * ((n-2)/2) = 1.5n-2.
# of comparisons when n is odd: 1 + 3 * ((n-3)/2) + 2 = 1.5n
Better Solution 3
class Pair { int min; int max; } public class Solution { public static Pair getMinMax(int arr[], int low, int high) { Pair result = new Pair(); Pair left = new Pair(); Pair right = new Pair(); // if there is only one element if (low == high) { result.max = arr[low]; result.min = arr[low]; return result; } // if there are two elements if (high == low + 1) { if (arr[low] > arr[high]) { result.max = arr[low]; result.min = arr[high]; } else { result.max = arr[high]; result.min = arr[low]; } return result; } // if there are more than 2 elements int mid = (low + high) / 2; left = getMinMax(arr, low, mid); right = getMinMax(arr, mid + 1, high); if (left.min < right.min) result.min = left.min; else result.min = right.min; if (left.max > right.max) result.max = left.max; else result.max = right.max; return result; } public static void main(String[] args) { int a1[] = { 3, 4, 2, 6, 8, 1, 9, 12, 15, 11 }; Pair result = getMinMax(a1, 0, a1.length - 1); System.out.println("Min: " + result.min); System.out.println("Max: " + result.max); } } |
# of Comparisons:
T(1) = 0 T(2) = 1 T(n) = T(floor(n/2)) + T(ceil(n/2)) + 2 ~ 2(T(n/2) + 2 = 1.5n -2
Conclusion
By optimization, we can reduce # of comparison from naive approach’s 2n to 1.5n.
I think he’s correct that its 1.5n, he’s already assuming that Math.min() and Math.max() use comparisons. For proof, look at Better Solution 2, in the “odd” case. we have +2 being added onto the end, and that would have come from one Math.min() and Math.max() in his example
Iterate through the array from beginning to end and compare the pairs of numbers. Find the local minimum and local maximum which takes (n/2) comparisons.
Find global minimum from (n/2) local minimums and global maximum from (n/2) local maximums which takes (n/2) comparisons each.
For more detail and code http://www.algoqueue.com/algoqueue/default/view/3997696/find-highest-and-lowest-element-in-an-array-with-minimum-comparisons
I was about to write the same response.. saw yours and just up voted.
For me the best solution (without using Arrays.sort(a1)) is your solution #1
With an array size of 10_000_000 int:
solution #1: do 9_999_999 comparision without creating addtional objects
solution #3: do 33_222_780 comparision and create 34_834_173 Pair objects
as a comparision were counted
if (low == high) {
if (high == low + 1) {
if (left.min right.max)
Math.min() and Math.max() internally also use comparisons. So, better solution is actually 3*n+1.