import java.util.*;
public class Solution {
/**
* 题意要求是TopK问题,时间复杂度达到O(NlogK),并且相同大小要按字典排序,
* 所以排序方法应该是堆排序或者快速选择排序。
* 这里排序使用小顶堆,按值排序时是从小到大。当值相同时,比较str,这里重写堆节点的比较方法,
* 值相同时,str字典序大的先入堆。
* 最后,排序好的小顶堆输出k个数到结果集,结果集数组从尾部开始填充,
* 这样小顶堆的数据刚好逆序,在结果集里的表现就是 按值排序是升序,值相同是字典小的在数组前面。
* return topK string
* @param strings string字符串一维数组 strings
* @param k int整型 the k
* @return string字符串二维数组
*/
public String[][] topKstrings (String[] strings, int k) {
if(strings==null || strings.length==0){
return null;
}
HashMap map = new HashMap();
for(String s : strings){
//初始化 数组中每个字符串默认出现一次
map.put(s,map.getOrDefault(s,0)+1);
}
// 优先队列,实现小顶堆,输出到结果集就是堆的逆序,即倒序输出小顶堆,即大顶堆
//https://blog.csdn.net/wufaliang003/article/details/82940218参考最小堆的思想
PriorityQueue minHeap = new PriorityQueue();
for(Map.Entry entrySet : map.entrySet()){
Node node = new Node(entrySet.getKey(),entrySet.getValue());
//先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
if(minHeap.size()
minHeap.add(node);
}else{
// 堆中元素等于 k 个时
// 当 node的值大于栈顶元素,或者值相同时node的字典小于栈顶元素 时(最小堆构建规则,就是如此)
//这相当于上一个条件:先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
//接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,
//以保证堆内的k个元素,总是当前最大的k个元素。
if(minHeap.peek().compareTo(node)
minHeap.poll();
minHeap.add(node);
}
}
}
String[][] result = new String[k][2];
//正序弹出小顶堆上面的元素,数组逆序输出
for(int i=k-1;i>=0;i--){
Node node = minHeap.poll();
result[i][0] = node.name;
result[i][1] = String.valueOf(node.count);
}
return result;
}
class Node implements Comparable{
//对应上面Map里的key
String name;
//对应上面Map里的value
int count;
public Node(String name,int count){
this.name = name;
this.count = count;
}
@Override
public int compareTo(Node node){
//正常是通过Node对象里的count来比较大小的
if(this.count > node.count){
return 1;
}else if(this.count
return -1;
}else{
//比如["2","2","1","1"]的情况 数组中字符串2出现2次 字符串1出现2次 这时候要求按字典顺序输出["1","2"]、["2","2"]1出现2次 2出现2次
//此时使用原生的比较器 用2个Node对象的string进行字典顺序比较
//上面的2个条件比较对象是堆顶的元素 被比较对象是要是否加入替代堆顶最小元素的元素 用的是count大小做比较
//但此时Node值相同,应该比较的是要加入的node的name字典值,小于栈顶元素 才会重新进行构建最小堆 应该反过来比较
return node.name.compareTo(this.name);
}
}
}
}