8

For example, given a List of Integer List<Integer> list = Arrays.asList(5,4,5,2,2), how can I get a maxHeap from this List in O(n) time complexity?

The naive method:

PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Collections.reverseOrder());
for (Integer i : list) {
    maxHeap.offer(i);
}

However, the time complexity is O(nlogn).

We can trigger the heapify method by using the following constructor:

PriorityQueue<Integer> maxHeap = new PriorityQueue<>(list);

The time complexity is O(n). However, it forces me to use the natural order which is the minHeap.

My Question:

How can I construct a PriorityQueue by heapifying a Collection with custom Comparator?

Ref: Java doc of PriorityQueue

PS: @user207421
Heapify algorithm can transform any unsorted array into a heap in O(n) time, not O(nlogn). There are many articles about heapify, also in CLRS's Introduction to Algorithms Page 159, build a heap from any unsorted array is O(n). And heap is also not a sorted array. It's a complete tree with heap property which can be encoded in array.

maplemaple
  • 1,297
  • 6
  • 24
  • At least in my version of the JDK, `heapify` is only called in `initFromCollection`, `bulkRemove` and `readObject`. None of those methods make sense to call here. For a quick and dirty solution, I would use reflection to set the comparator. Otherwise, roll your own implementation. – Sweeper Aug 28 '21 at 00:31
  • If the input collection is already sorted the complexity is *O(N)*. However if it isn't sorted, it has to be sorted, so the time must be *O(N log(N))*, whichever way you cut it. You can't get sorting for nothing. Question doesn't really make sense. – user207421 Aug 28 '21 at 05:05
  • @user207421 Have you ever known the heapify algorithm? It can take any unsorted array into a heap in `O(n)` time not `O(nlog n)`. By the way, heap is also not a fully sorted array. – maplemaple Aug 28 '21 at 07:45
  • @user207421 https://www.geeksforgeeks.org/time-complexity-of-building-a-heap/ There are bunch of articles about heapify which is `O(n)` to heapify any unsorted array. – maplemaple Aug 28 '21 at 07:48
  • @maplemaple I've been using heapsort since 1971. The claims in your links have exactly the same status as my claim above: they are unrefereed Internet junk. If you were to quote Dijkstra or Knuth or Wirth or Hoare or Sedgewick at me it would be another matter. I see from my library that Sedgewick states that heapify can be *O(N)* in most cases, but not in all. – user207421 Aug 28 '21 at 10:01
  • Heap construction from the beginning can be ```O(n)``` as stated by Sedgewick: "Sink-based heap construction uses fewer than 2N compares and fewer than N exchanges to construct a heap from N items." However, creating an empty PQ and then inserting into it one-by-one requires ```O(n logn)``` time complexity. Priority Queue implementation of Java doesn't accept a custom ```Comparator``` and ```Collection``` at the same time. So OP asks, how can we create a PQ in ```O(n)``` with our custom ```Comparator``` so that it is max-heap not min-heap (as default). – Muhteva Aug 28 '21 at 10:08
  • @user207421 Muhteva has cited the argument of Sedgewick. In CLRS's Introduction to Algorithms Page 159, build a heap from any unsorted array is O(n) – maplemaple Sep 12 '21 at 07:18

2 Answers2

4

If you don't mind some hack

According to the java doc of PriorityQueue(PriorityQueue)

Creates a PriorityQueue containing the elements in the specified priority queue. This priority queue will be ordered according to the same ordering as the given priority queue.

So we can extend PriorityQueue as CustomComparatorPriorityQueue to hold the desired comparator and the Collection we need to heapify. Then call new PriorityQueue(PriorityQueue) with an instance of CustomComparatorPriorityQueue.

Below is tested to work in Java 15.

import java.util.*;

public class CustomComparatorPriorityQueue<T> extends PriorityQueue<T> {
    private Collection<T> wrapped;

    public static <U> PriorityQueue<U> create(Collection<U> wrapped, Comparator<U> custom) {
        return new PriorityQueue<U>(new CustomComparatorPriorityQueue<>(wrapped, custom));
    }

    private CustomComparatorPriorityQueue(Collection<T> wrapped, Comparator<T> custom) {
        super(custom);
        this.wrapped = wrapped;
    }

    @Override
    public Object[] toArray() {
        return wrapped.toArray();
    }

    public static void main(String[] args) {
        List<Integer> a = Arrays.asList(3, 6, 4, 8, 1, 9);
        PriorityQueue<Integer> pq = CustomComparatorPriorityQueue.create(a, Comparator.<Integer>naturalOrder().reversed());
        Integer b;
        while ((b = pq.poll()) != null) {
            System.out.println(b);
        }
    }

    // Override to don't allow other purpose...
}

samabcde
  • 6,988
  • 2
  • 25
  • 41
0

One approach is to implement Max Heap via Min Heap: multiply each number in the collection by -1, then heapify the new collection into a Min Heap, and when we retrieve the number from the heap, also multiply by -1 to get the original number.

echo
  • 71
  • 1
  • 3