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

后端 未结 14 1419
深忆病人
深忆病人 2020-12-04 09:08

This is a interview question: given an array of integers find the max. and min. using minimum comparisons.

Obviously, I can loop over the array twice and use ~

相关标签:
14条回答
  • 2020-12-04 09:29

    A simple pseudo code for the recursive algorithm:

    Function MAXMIN (A, low, high)
        if (high − low + 1 = 2) then 
          if (A[low] < A[high]) then
             max = A[high]; min = A[low].
             return((max, min)).
          else
             max = A[low]; min = A[high].
             return((max, min)).
          end if
       else
          mid = low+high/2
          (max_l , min_l ) = MAXMIN(A, low, mid).
          (max_r , min_r ) =MAXMIN(A, mid + 1, high).
       end if
    
       Set max to the larger of max_l and max_r ; 
       likewise, set min to the smaller of min_l and min_r .
    
       return((max, min)).
    
    0 讨论(0)
  • 2020-12-04 09:36

    A somewhat different approach, which uses integer arithmetic instead of comparisons (which wasn't explicitly prohibited)

    for(int i=0;i<N;i++) {
      xmin += x[i]-xmin & x[i]-xmin>>31;
      xmax += x[i]-xmax & xmax-x[i]>>31;
    }
    
    0 讨论(0)
  • 2020-12-04 09:37

    After reading the question and answers, I decided to try a few versions (in C#).
    I thought the fastest would be Anton Knyazyev's one (branch free), it isn't (on my box).
    Results:

    /*                comp.  time(ns)
          minmax0     3n/2    855
          minmax1     2n      805
          minmax2     2n     1315 
          minmax3     2n      685          */
    

    Why are minmax1 and minmax3 faster? Probably because the "branch predictor" does a nice job, each iteration the chance, a new min (or max) is found, decreases, so predictions become better.
    All in all it's a simple test. I do realize my conclusions may be:
    -premature.
    -not valid for different platforms.
    Let's say they're indicative.
    Edit: Break-even point minmax0, minmax3: ~100 items,
    10,000 items: minmax3 ~3.5 times faster than minmax0.

    using System;
    using sw = System.Diagnostics.Stopwatch;
    class Program
    {
        static void Main()
        {
            int n = 1000;
            int[] a = buildA(n);
            sw sw = new sw();
            sw.Start();
            for (int i = 1000000; i > 0; i--) minMax3(a);
            sw.Stop();
            Console.Write(sw.ElapsedMilliseconds);
            Console.Read();
        }
    
        static int[] minMax0(int[] a)  // ~3j/2 comp.
        {
            int j = a.Length - 1;
            if (j < 2) return j < 0 ? null :
                j < 1 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a[1], ai = a0;
            if (a1 < a0) { a0 = a1; a1 = ai; }
            int i = 2;
            for (int aj; i < j; i += 2)
            {
                if ((ai = a[i]) < (aj = a[i + 1]))  // hard to predict
                { if (ai < a0) a0 = ai; if (aj > a1) a1 = aj; }
                else
                { if (aj < a0) a0 = aj; if (ai > a1) a1 = ai; }
            }
            if (i <= j)
            { if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax1(int[] a)  // ~2j comp.  
        {
            int j = a.Length;
            if (j < 3) return j < 1 ? null :
                j < 2 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a0, ai = a0;
            for (int i = 1; i < j; i++)
            {
                if ((ai = a[i]) < a0) a0 = ai;
                else if (ai > a1) a1 = ai;
            }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax2(int[] a)  // ~2j comp.  
        {
            int j = a.Length;
            if (j < 2) return j == 0 ? null : new int[] { a[0], a[0] };
            int a0 = a[0], a1 = a0;
            for (int i = 1, ai = a[1], aj = ai; ; aj = ai = a[i])
            {
                ai -= a0; a0 += ai & ai >> 31;
                aj -= a1; a1 += aj & -aj >> 31;
                i++; if (i >= j) break;
            }
            return new int[] { a0, a1 };
        }
    
        static int[] minMax3(int[] a)  // ~2j comp.
        {
            int j = a.Length - 1;
            if (j < 2) return j < 0 ? null :
                j < 1 ? new int[] { a[0], a[0] } :
                a[0] < a[1] ? new int[] { a[0], a[1] } :
                              new int[] { a[1], a[0] };
            int a0 = a[0], a1 = a[1], ai = a0;
            if (a1 < a0) { a0 = a1; a1 = ai; }
            int i = 2;
            for (j -= 2; i < j; i += 3)
            {
                ai = a[i + 0]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
                ai = a[i + 1]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
                ai = a[i + 2]; if (ai < a0) a0 = ai; if (ai > a1) a1 = ai;
            }
            for (j += 2; i <= j; i++)
            { if ((ai = a[i]) < a0) a0 = ai; else if (ai > a1) a1 = ai; }
            return new int[] { a0, a1 };
        }
    
        static int[] buildA(int n)
        {
            int[] a = new int[n--]; Random rand = new Random(0);
            for (int j = n; n >= 0; n--) a[n] = rand.Next(-1 * j, 1 * j);
            return a;
        }
    }
    
    0 讨论(0)
  • 2020-12-04 09:39

    go for divide and conquer !

    1,3,2,5

    for this finding min,max will take 6 comparisons

    but divide them

    1,3 ---> will give min 1 and max 3 in one comparison
    2,5 ---> will give min 2 and max 5 in one comparison 
    

    now we can compare two mins and maxs

    min(1,2) --> will give the final min as 1 (one comparison)
    max(3,5) ---> will give the final max as 5 (one comparison)
    

    so totally four comparisons to find both min and max.

    0 讨论(0)
  • 2020-12-04 09:40

    Trying to improve on the answer by srbh.kmr. Say we have the sequence:

    A = [a1, a2, a3, a4, a5]
    

    Compare a1 & a2 and calculate min12, max12:

    if (a1 > a2)
      min12 = a2
      max12 = a1
    else
      min12 = a1
      max12 = a2
    

    Similarly calculate min34, max34. Since a5 is alone, keep it as it is...

    Now compare min12 & min34 and calculate min14, similarly calculate max14. Finally compare min14 & a5 to calculate min15. Similarly calculate max15.

    Altogether it's only 6 comparisons!

    This solution can be extended to an array of arbitrary length. Probably can be implemented by a similar approach to merge-sort (break the array in half and calculate min max for each half).

    UPDATE: Here's the recursive code in C:

    #include <stdio.h>
    
    void minmax (int* a, int i, int j, int* min, int* max) {
      int lmin, lmax, rmin, rmax, mid;
      if (i == j) {
        *min = a[i];
        *max = a[j];
      } else if (j == i + 1) {
        if (a[i] > a[j]) {
          *min = a[j];
          *max = a[i];
        } else {
          *min = a[i];
          *max = a[j];
        }
      } else {
        mid = (i + j) / 2;
        minmax(a, i, mid, &lmin, &lmax);
        minmax(a, mid + 1, j, &rmin, &rmax);
        *min = (lmin > rmin) ? rmin : lmin;
        *max = (lmax > rmax) ? lmax : rmax;
      }
    }
    
    void main () {
      int a [] = {3, 4, 2, 6, 8, 1, 9, 12, 15, 11};
      int min, max;
      minmax (a, 0, 9, &min, &max);
      printf ("Min : %d, Max: %d\n", min, max);
    }
    

    Now I cannot make out the exact number of comparisons in terms of N (the number of elements in the array). But it's hard to see how one can go below this many comparisons.

    UPDATE: We can work out the number of comparisons like below:

    At the bottom of this tree of computations, we form pairs of integers from the original array. So we have N / 2 leaf nodes. For each of these leaf nodes we do exactly 1 comparison.

    By referring to the properties of a perfect-binary-tree, we have:

    leaf nodes (L) = N / 2 // known
    total nodes (n) = 2L - 1 = N - 1
    internal nodes = n - L = N / 2 - 1
    

    For each internal node we do 2 comparisons. Therefore, we have N - 2 comparisons. Along with the N / 2 comparisons at the leaf nodes, we have (3N / 2) - 2 total comparisons.

    So, may be this is the solution srbh.kmr implied in his answer.

    0 讨论(0)
  • 2020-12-04 09:40
    #include<bits/stdc++.h>
    using namespace std;
    int main()
    {
        int n;
        cin>>n;
        set<int> t;
        for(int i=0;i<n;i++)
        {
    
            int x;
            cin>>x;
            t.insert(x);
        }
        set<int>::iterator s,b;
        s=t.begin();
        b=--t.end();
        cout<< *s<<" "<<*b<<endl;
    
    
        enter code here
    
        return 0;
    }
    

    // this can be done in log(n) complexity!!!

    0 讨论(0)
提交回复
热议问题