1

I have to construct a max-heap out of an array (called nums in the following code) and so I'm using java.util.PriorityQueue.

My code looks like this:

PriorityQueue<Integer> pq = new PriorityQueue<>(nums.length, (a, b) -> b - a);
for (int i = 0; i < nums.length; i++) {
    pq.offer(nums[i]);
}

I'm trying to find the time complexity (in terms of Big-O notation) of the above for loop.

I understand that PriorityQueue don't specify the details of the growth of underlying data structure. (And in worst-case it can be O(n) when expanding the internal-array and copying all the elements over the newly allocated space).

But I assume that when I specify the initialCapacity and don't add elements more than this initialCapacity, then the worst case time complexity of the above loop should be O(n) instead of O(nlog(n)). I understand from here that building time of heap is O(n) and nlog(n) is a loose upper bound.

Am I correct, or am I missing something?

I just want to understand that if I configure my PriorityQueue with initialCapacity of n and add n elements in that priority-queue, what will be the time complexity of this building-heap process?

PS: I already saw this, but answers for this question just claim things without explanation and may be they are not so Java specific.

I also see that java.util.PriorityQueue has a constructor that takes in a Collection. What will be the time complexity of this constructor? Shouldn't it be O(n)?

Lavish Kothari
  • 1,580
  • 14
  • 23
  • prioriyQueue works in O(logn) and total is O(log(n) * num.length) – Jahongir Sabirov Feb 03 '19 at 09:11
  • @JahongirSabirov I want to know (with explanation) that with my specific implementation, should I assume that it works in `O(nlog(n))` or the JDK implementation of `PriorityQueue` takes advantage and I get `O(n)` when I specify the `initialCapacity` – Lavish Kothari Feb 03 '19 at 09:14
  • 3
    You are asking questions that depend on the details of the implementation of `PriorityQueue`. If you really want to understand how your code will perform, you need to do the complexity analysis yourself ... for a specific implementation. Fortunately, the source code is freely available. – Stephen C Feb 03 '19 at 09:32

1 Answers1

2

I understand that PriorityQueue don't specify the details of the growth of underlying data structure.

Let us be clear about this. The javadoc states that the policy for expanding the queue is unspecified.

(And in worst-case it can be O(n) when expanding the internal-array and copying all the elements over the newly allocated space).

The current policy (Java 11) is:

    // Double size if small; else grow by 50%
    int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                     (oldCapacity + 2) :
                                     (oldCapacity >> 1));

The amortized cost is O(1) per insertion for a "double" policy. For grow by 50% is is not as good. But is is way better than O(n).

It is safe to assume that they wouldn't unilaterally change this policy to something with substantially worse complexity, no matter what the current specification (technically) permits.

However, this is not germane to your question since you are using initialCapacity capacity, either explicitly, or when you populate the PriorityQueue from a collection.

I assume that when I specify the initialCapacity and don't add elements more than this `initialCapacity, then the worst case time complexity of the above loop should be O(n) instead of O(nlog(n)). I understand from here that building time of heap is O(n) and nlog(n) is a loose upper bound.

Am I correct, or am I missing something?

I think you are missing something.

Assuming that your input array is unsorted, building the heap ("heapification") and retrieving the elements in order is equivalent to sorting the elements into priority order. That is an O(nlogn) operation on average. While the heapification itself is O(n) (since the code uses sift down heapification), you have actually put off some of the sorting cost until later.

So unless you only intend to retrieve a non O(n) subset of the elements that you put into the queue, the overall answer is O(nlogn).

I just want to understand that if I configure my PriorityQueue with initialCapacity of n and add n elements in that priority-queue, what will be the time complexity of this building-heap process?

The overall complexity (adding and removing n elements) will be O(nlogn) for the reason stated above.

I also see that PriorityQueue has a constructor that takes in a Collection. What will be the time complexity of this constructor? Shouldn't it be O(n)?

If the collection is unsorted, then the elements must be heapified; see above. There is some special-case code to deal with a SortedCollection which skips the heapification step.


Notes:

  1. You can confirm the details above by reading the source code for PriorityQueue. Google can find it for you.
  2. The Wikipedia page on HeapSort talks about heapification
  3. The proof that growing by doubling an array-based data structure is O(1) per insertion is given in good algorithms text books. The same analysis could be applied to growing by 50%.
  4. Your lambda expression (a, b) -> b - a is not correct for ordering integers unless they are positive.
Community
  • 1
  • 1
Stephen C
  • 632,615
  • 86
  • 730
  • 1,096
  • Can you please tell me what exactly is the problem? Are you talking about overflows? yes in case of overflow, this lamda can misbehave. Is there anything else that's wrong with this lamda other then the overflow condition? – Lavish Kothari Feb 03 '19 at 11:27
  • 1
    Well ... if you get an overflow, then the comparator gives an incorrect answer, and the ordering breaks => incorrectly ordered queue. – Stephen C Feb 03 '19 at 11:40
  • "Assuming that your input array is unsorted, building the heap ("heapification") entails sorting the elements into priority order." Really? I don't think building heap will actually sort elements in priority order. `[1, 2, 3, 5, 12, 11, 10, 7]` is a not sorted and is still heapified (and is a min-heap). – Lavish Kothari Feb 03 '19 at 11:48
  • `There is some special-case code to deal with a SortedCollection which skips the heapification step` I see but [`PriorityQueue​(SortedSet‹? extends E› c)`](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html#%3Cinit%3E(java.util.SortedSet). I wished for more than a decade there *was* something like *`java.util.SortedCollection`* (and a PriorityQueue constructor allowing a collection *and* a Comparator). – greybeard Feb 03 '19 at 12:31
  • 1
    `You don't need to supply a Comparator for Integer elements: the Integer class implements Comparable` You need to if you want inverted priority - `(a, b) -> b.compareTo(a)`. – greybeard Feb 03 '19 at 12:37
  • @greybeard - You are free to submit patches to the OpenJDK project. – Stephen C Feb 03 '19 at 14:28