Search code examples
javaalgorithmperformancepriority-queue

Why is my PriorityQueue implementation slower than sorting for the kth largest integer problem?


I implemented two solutions to the kth largest integer problem, where we are given an array of integers and have to return the kth largest integer in the array. The first one used a PriorityQueue, and has a runtime complexity of O(n). The second simply sorted the array and returned the kth element, and has a runtime complexity of O(nlogn).

However when I benchmarked both implementations against a large array the sort method was significantly faster than the PriorityQueue method for many different values of k, for example when k is half the size of the array.

What could I be doing wrong in my PriorityQueue implementation that is causing it to be slower than the sort implementation?

Here is the code I have:

import java.util.PriorityQueue;
import java.util.Random;

public class KthLargestElement {

    public static void main(String[] args) {
        int n = 100000000;
        int k = n/2;
        int[] numbers = intsInRange(n, 0, n*100);

        long start = System.currentTimeMillis();
        kthLargestPriorityQueueMethod(numbers, k);
        System.out.println("priority queue method time in seconds = " + (double)(System.currentTimeMillis() - start)/1000.0);

        start = System.currentTimeMillis();
        kthLargestSortMethod(numbers, k);
        System.out.println("sort method time in seconds = " + (double)(System.currentTimeMillis() - start)/1000.0);
    }

    private static int kthLargestSortMethod(int[] numbers, int k) {
        Arrays.sort(numbers);
        return numbers[numbers.length - k];
    }

    private static int kthLargestPriorityQueueMethod(int[] numbers, int k) {
        PriorityQueue<Integer> q = new PriorityQueue<>();
        for(int i : numbers){
            q.offer(i);

            if(q.size() > k){
                q.poll();
            }
        }

        return q.peek();
    }

    private static int[] intsInRange(int size, int lowerBound, int upperBound) {
        Random random = new Random();
        int[] result = new int[size];
        for (int i = 0; i < size; i++) {
            result[i] = random.nextInt(upperBound - lowerBound) + lowerBound;
        }
        return result;
    }
}

And here is the output I get on my computer:

priority queue method time in seconds = 78.194
sort method time in seconds = 9.864

Solution

  • The bug is in your understanding, not your code.

    The priority queue approach is O(n log(k)) running time with constants that aren't as good as the sort. So when log(k) = log(n) + O(1), then it is no surprise that the priority queue can be slower.

    You can get an improvement by only bothering with the offer/poll when the the current element won't be the one polled. This improvement will be more significant if k is small relative to n.