I was trying out the problem Top K Frequent Elements and I am not asking for how its implemented but what is going wrong in my implementation.
My implementation idea:
- Declare a
HashMapto count the frequency of each number. - Declare a max heap with a
PriorityQueueto keep track of the most frequent K elements. - Add elements to the
HashMap. - Use a custom
Comparatorin thePriorityQueueso that it sorts its elements on the basis of the frequency from theHashMap.
My code is as follows:
public class TopKFrequentElements {
private final HashMap<Integer, Integer> freqMap = new HashMap<>();
private final PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(
(a, b) -> {
System.out.printf("a = %d b = %d, Freq(%d) = %d Freq(%d) = %d, Freq(%d).compareTo(Freq(%d)) = %d\n", a, b,
a, freqMap.get(a), b, freqMap.get(b), b, a, freqMap.get(b).compareTo(freqMap.get(a)));
return freqMap.get(b).compareTo(freqMap.get(a));
}
);
private void add(int num) {
freqMap.put(num, freqMap.getOrDefault(num, 0) + 1);
System.out.println("num = " + num);
priorityQueue.add(num);
System.out.println("--------------");
}
private int[] getK(int k) {
int[] res = new int[k];
int ct = 0;
while (ct < k) {
res[ct] = priorityQueue.peek();
freqMap.put(res[ct], freqMap.get(res[ct]) - 1);
priorityQueue.poll();
while (!priorityQueue.isEmpty() &&
priorityQueue.peek()==res[ct]) {
freqMap.put(res[ct], freqMap.get(res[ct]) - 1);
priorityQueue.poll();
}
ct++;
}
return res;
}
public int[] topKFrequent(int[] nums, int k) {
for (int num : nums) {
add(num);
}
return getK(k);
}
public static void main(String[] args) {
TopKFrequentElements elements = new TopKFrequentElements();
System.out.println(Arrays.toString(
elements.topKFrequent(new int[]{3,0,1,0}, 1))
);
}
}
The code works as expected for all test cases expect this really interesting one:
{3,0,1,0} k = 1
Here the output is [3] where it should be [0].
I don't understand why this is happening, when the last 0 is added, then the Comparator of the PriorityQueue should preserve the invariant. But it does not.
Digging deeper I printed out when the Comparator is called and I see that it is not comparing with all the elements. Which I am unable to understand why.
num = 3
--------------
num = 0
a = 0 b = 3, Freq(0) = 1 Freq(3) = 1, Freq(3).compareTo(Freq(0)) = 0
--------------
num = 1
a = 1 b = 3, Freq(1) = 1 Freq(3) = 1, Freq(3).compareTo(Freq(1)) = 0
--------------
num = 0
a = 0 b = 0, Freq(0) = 2 Freq(0) = 2, Freq(0).compareTo(Freq(0)) = 0
As you can see the last addition for 0 is just calling the Comparator once when it should check with other values according to my intuition.
Where are the calls for 3 and 1 ?
What am I doing wrong? I also cannot replicate this problem for any other example.
What have I missed about the PriorityQueue implementation?
If the priority of an item is going to change on the fly, you need to remove the item from the queue and add it back, so that the
PriorityQueuewill reflect the change.A more efficient way to approach it would be to calculate all the statistics for the priorities in advance, and then populate the priority queue. But you are also altering the priorities with each item you remove from the queue, so while this won't in your case, it would work in a similar case where the priorities are static.
Example
For a given set of data
With four 2s they are they highest priority. Three 3s come next. At two apiece, the 1s and 6s are level pegging. The 4 is the lowest priority, with only a single entry.
Output