1

Doing some leetcoding. Top K frequent elements. I'm implementing my solution via Max Heap. Can someone please help me understand the PriorityQueue comparator syntax?

Map<Integer, Integer> map = new HashMap<>();

//values are added to hashMap....

Queue<Integer> heap = new PriorityQueue<>((a,b) -> map.get(b) - map.get(a));

What is happening in the instantiation of the PriorityQueue in the above code? How are elements a and b being compared?

I know a max Heap structure is being created, but I want to know what is happening in the code and how the comparator syntax reflects that.

Thanks!!

5
  • 7
    b - a gives you a negative value if a is greater and a positive value if b is greater, except when it doesn't. You could replace the lambda with Comparator.comparing(map::get). Commented Aug 6, 2024 at 1:16
  • If you didn't understand the previous comment, @shmosel is saying (correctly!) that the b - a comparator is a bug. Commented Aug 6, 2024 at 3:02
  • 1
    It's not technically a bug here, since counts are guaranteed to be nonnegative; the subtraction trick is safe when the inputs are nonnegative. It's unclear and a bad idea, but not a bug. Commented Aug 6, 2024 at 4:28
  • @LouisWasserman until the counting is somehow normalized for example between -100 and 100 (for reasons we don't know, e.g. plotting); or some developer copy & paste that code for a different use case Commented Aug 6, 2024 at 8:02
  • 1
    (Obligatory: normalization between -100 and 100 would also be safe and not break that comparator.) Commented Aug 6, 2024 at 15:45

4 Answers 4

3
  1. A constructor call to PriorityQueue accepts a Comparator.

new PriorityQueue<>((a,b) -> map.get(b) - map.get(a));

  1. Comparator is defined as a FunctionalInterface.

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

  1. The abstract method of the functional interface is,

int compare(T o1, T o2)

...where T is the type within the queue, in this case, Integer. In other words, the method requires two arguments of the same type as input and produces an int as output (note: this is the same general method shape as a BiFunction or ToIntBiFunction).

  1. The lambda expression provided is,

(a,b) -> map.get(b) - map.get(a)

...which satisfies the abstract method signature as,

(Integer a, Integer b) -> map.get(b) - map.get(a)

...by looking up the (Integer) values corresponding to both a and b in the previously-instantiated HashMap and subtracting one Integer from the other to return an int. Subtraction results in the higher value being "prioritized".

  1. This implementation assumes every Integer added to the queue has already been added as a key in the map, with a corresponding, non-null value; otherwise, the Comparator throws a NullPointerException.
Sign up to request clarification or add additional context in comments.

1 Comment

This is a fine answer but it could be majorly improved in one way: The code OP posted is really bad code, a - b is dangerous: It's broken in a way that is easy to miss in a unit test. The code should be using Comparator.comparingInt(a -> map.get(a)) instead.
2

If the result of map.get(b) - map.get(a) is positive, which means that b has a higher frequency than a. In the context of a PriorityQueue, this means that b should be prioritized (or placed higher in the heap) than a.

If the result is negative, a has a higher frequency than b, so a should be prioritized.

If the result is zero, they have the same frequency, and their order is not changed relative to each other (but in the max heap, one will still take precedence over the other based on their insertion order).

1 Comment

Except that b - a is a bug. See stackoverflow.com/questions/2728793
0

The constructor is declared as PriorityQueue(Comparator<? super E> comparator), since Comparator is a functional interface, the lambda expression (a,b) -> map.get(b) - map.get(a) is creating an anonymous Comparator similar to:

new Comparator<Integer>() {

    @Override
    public int compare(Integer a, Integer b) {
        return map.get(b) - map.get(a);
    }
}

Note 1: but no class (.class file) is created, as would be the case for an anonymous class
Note 2: as mentioned multiple times, comparing int values using subtraction can lead to errors (overflow/underflow);
it is recommended to use the static Integer.compare() method instead

Comments

0

Queue heap = new PriorityQueue<>((a, b) -> map.get(b) - map.get(a));

creates a max-heap PriorityQueue where the priority is determined by the values associated with each integer in the map. The comparator (a, b) -> map.get(b) - map.get(a) ensures that the element with the higher value in the map comes first in the queue. This way, the queue prioritizes elements based on their associated values in descending order.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.