I recently attended an interview where I was asked "write a program to find 100 largest numbers out of an array of 1 billion numbers."
I was only able to give a brute force solution which was to sort the array in O(nlogn) time complexity and take the last 100 numbers.
Arrays.sort(array);
The interviewer was looking for a better time complexity, I tried a couple of other solutions but failed to answer him. Is there a better time complexity solution?
Use Heap building the MAX heap will take O(N),then the top 100 max numbers will be in the top of the Heap, all you need is to get them out from the heap(100 X O(Log(N)).
To find the largest element, the first two elements of array are checked and the largest of these two elements are placed in arr[0] the first and third elements are checked and largest of these two elements is placed in arr[0] . this process continues until the first and last elements are checked.
Take two variables and initiliaze them with zero. Iterate through each element of the array and compare each number against these two number. If current number is greater than maxOne then maxOne = number and maxTwo = maxOne. Otherwise if it only greater than maxTwo then we only update maxTwo with current number.
Solution Approach For the largest three elements, we will create three elements holding their values, max, max2 and max3 and set these values to arr[0]. if (arr[i] > max) -> max3 = max2, max2 = max , max = arr[i]. else if (arr[i] > max2) -> max3 = max2, max2 = arr[i]. else if (arr[i] > max3) -> max3 = arr[i].
You can keep a priority queue of the 100 biggest numbers, iterate through the billion numbers, whenever you encounter a number greater than the smallest number in the queue (the head of the queue), remove the head of the queue and add the new number to the queue.
EDIT: As Dev noted, with a priority queue implemented with a heap, the complexity of insertion to queue is O(log N)
In the worst case you get billion*log2(100)
which is better than billion*log2(billion)
In general, if you need the largest K numbers from a set of N numbers, the complexity is O(N log K)
rather than O(N log N)
, this can be very significant when K is very small comparing to N.
EDIT2:
The expected time of this algorithm is pretty interesting, since in each iteration an insertion may or may not occur. The probability of the i'th number to be inserted to the queue is the probability of a random variable being larger than at least i-K
random variables from the same distribution (the first k numbers are automatically added to the queue). We can use order statistics (see link) to calculate this probability. For example, lets assume the numbers were randomly selected uniformly from {0, 1}
, the expected value of (i-K)th number (out of i numbers) is (i-k)/i
, and chance of a random variable being larger than this value is 1-[(i-k)/i] = k/i
.
Thus, the expected number of insertions is:
And the expected running time can be expressed as:
(k
time to generate the queue with the first k
elements, then n-k
comparisons, and the expected number of insertions as described above, each takes an average log(k)/2
time)
Note that when N
is very large comparing to K
, this expression is a lot closer to n
rather than N log K
. This is somewhat intuitive, as in the case of the question, even after 10,000 iterations (which is very small comparing to a billion), the chance of a number to be inserted to the queue is very small.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With