49
$\begingroup$

To find the median of an unsorted array, we can make a min-heap in $O(n\log n)$ time for $n$ elements, and then we can extract one by one $n/2$ elements to get the median. But this approach would take $O(n \log n)$ time.

Can we do the same by some method in $O(n)$ time? If we can, then how?

$\endgroup$
53
$\begingroup$

This is a special case of a selection algorithm that can find the $k$th smallest element of an array with $k$ is the half of the size of the array. There is an implementation that is linear in the worst case.

Generic selection algorithm

First let's see an algorithm find-kth that finds the $k$th smallest element of an array:

find-kth(A, k)
  pivot = random element of A
  (L, R) = split(A, pivot)
  if k = |L|+1, return pivot
  if k ≤ |L|  , return find-kth(L, k)
  if k > |L|+1, return find-kth(R, k-(|L|+1))

The function split(A, pivot) returns L,R such that all elements in R are greater than pivot and L all the others (minus one occurrence of pivot). Then all is done recursively.

This is $O(n)$ in average but $O(n^2)$ in the worst case.

Linear worst case: the median-of-medians algorithm

A better pivot is the median of all the medians of sub arrays of A of size 5, by using calling the procedure on the array of these medians.

find-kth(A, k)
  B = [median(A[1], .., A[5]), median(A[6], .., A[10]), ..]
  pivot = find-kth(B, |B|/2)
  ...

This guarantees $O(n)$ in all cases. It is not that obvious. These powerpoint slides are helpful both at explaining the algorithm and the complexity.

Note that most of the time using a random pivot is faster.

| cite | improve this answer | |
$\endgroup$
  • $\begingroup$ Is this size 5 standard ? What if size of A is less than 5 ? $\endgroup$ – Jayesh Feb 21 '16 at 20:02
  • $\begingroup$ For any fixed n, the complexity is constant, unless it is infinite. So, you can use any valid algorithm with finite complexity for such special case, even if it was O(2^n). For a fixed n (i.e. at most 4 in out case), the complexity is at most O(2^4) = O(1). $\endgroup$ – v6ak Apr 23 '16 at 18:42
  • 3
    $\begingroup$ On the first algorithm: return A[k] is incorrect (unless A is sorted which would make the algorithm moot). If split happened to divide A such that k = |L| + 1 you still don't know where the kth element is. Your base case is when |A| = 1 else you still need to make one of the two recursive calls. $\endgroup$ – wcochran Apr 30 '16 at 4:48
  • 2
    $\begingroup$ @NickCaplinger fixed using web.archive.org $\endgroup$ – jmad May 17 '16 at 1:06
  • 1
    $\begingroup$ Isn't the worst case for the generic selection algorithm O(NlogN)? Even if the recursive call leaves only 10% of the array after each call, then it's still a logarithm in basis 10. $\endgroup$ – octavian Mar 29 '17 at 14:40
6
$\begingroup$

There is a randomized Monte Carlo algorithm to do this. It is described in [MU2017]. It is a linear time algorithm that fails to produce a solution with probability at most $n^{-1/4}$. As discussed in [MU2017], it is significantly simpler than the deterministic $O(n)$ algorithm and yields a smaller constant factor in the linear running time.

The main idea of the algorithm is to use sampling. We have to find two elements that are close together in the sorted order of the array and that have the median lie between them. See the reference [MU2017] for a complete discussion.


[MU2017] Michael Mitzenmacher and Eli Upfal. "Probability and Computing: Randomization and Probabilistic Techniques in Algorithms and Data Analysis," chapter 3, pages 57-62. Cambridge University Press, Second edition, 2017.

| cite | improve this answer | |
$\endgroup$
1
$\begingroup$

Remember the Quicksort algorithm?

The difference is that you don't need the whole array sorted, you only need the portion containing the median in the right place. If you have n elements 0 to n-1, then the median is element (n - 1)/2 if n is even, and the average of elements n/2 - 1 and n/2 if n is odd.

So you start with one quicksort partition. Then you look at the two partitions: Instead of sorting them both, you only partition the one containing the one or two elements that you are interested in, and that half gets partitioned further.

(In the unlucky case that you end up with two partitions, one ending at n/2 - 1 and one starting at n, you can just take the maximum and minimum of each side)

You can easily adapt this method to find the k largest or k smallest items, or items with index k to k' in linear time.

| cite | improve this answer | |
$\endgroup$
  • $\begingroup$ (Is this a useful addition to the accepted answer?) $\endgroup$ – greybeard Feb 1 at 12:40
  • $\begingroup$ It tells you how you can find an algorithm yourself. That's much more important than being given the algorithm. $\endgroup$ – gnasher729 Feb 2 at 15:56
0
$\begingroup$

I have one more solution for Find median of unsorted array in O(n) time but it will Increase Space Complexity.

#include<stdio.h>
#include<conio.h>

int main()
{
    int a[11] = {10,45,35,3,24,23,78,89,33,34,11};
    int max=0,*ar,co=0,mid=0;
    for(int i=0; i<sizeof(a)/4; i++)   // O(n)
    {
        if(i==0)
            max = a[i];
        if(a[i]>max)
            max = a[i];
    }
    ar =(int*)malloc((sizeof(int)*max)+1); // O(n)
    for(int i=0; i<sizeof(a)/4; i++)
        ar[a[i]] = 1;
    for(int i=0; i<=max; i++)  // 0(n)
        if(ar[i]==1)
        {
            co++;
            if(co == ((sizeof(a)/8)+1))
                mid = i;
        }
    printf("%d",mid);
}
//Total = n+n+n = O(n)
| cite | improve this answer | |
$\endgroup$
  • 1
    $\begingroup$ I appreciate your participation. However, we're not a coding site and we're not looking for answers that consist entirely or primarily of code. Instead, we're looking for ideas, concise pseudocode, explanation, justification, etc. Also, not everyone here reads C code. I encourage you to edit your question accordingly. Thank you. $\endgroup$ – D.W. Feb 1 at 6:09
  • 1
    $\begingroup$ Please include an argument how the last loop requires $O(n)$ operations. $\endgroup$ – greybeard Feb 1 at 8:54
  • $\begingroup$ The memory allocation is $O(k)$, where $k$ is the $\max (a)$. Since each number is represented with $O(n)$ bits, $k$ might be exponential in $n$. This also means the last loop is $O(2^n)$ like greybeard suggested. $\endgroup$ – Shahaf Finder Feb 1 at 12:46
  • $\begingroup$ @sejpalsinh $n$ means the number of element in the given array. It does NOT mean the max or an upper bound of the numbers in the given array. $\endgroup$ – John L. Feb 1 at 14:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.