输入:
4 2
1
2
3
4
解释:4表示接下来输入4个字符串,2表示我们要找出现次数前2多和出现次数前2少的字符串,如果次数相同,根据字典序排序,字典序小的放前面。
然后下面的每一行都是输入的一个字符串。
因此输出为:
1 1
2 1
1 1
2 1
思路,本来遇到这种topk的自然想到小跟堆,但是最后输出是要按照出现次数进行排序的,小跟堆只能做到输出最大的k个,但是这k个并不会给你排好序,所以我就老老实实使用Arrays.sort()了,就是复制一下数组,然后分别应用两个比较器,一个比较器是:次数大的放前面,次数相同就根据字典序排序,另外一个比较器是:次数小的放前面,次数相同就根据字典序排序。
然后如何统计每个字符串的频率呢?这里我使用了一个hashmap,key是这个字符串,value是一个node类型,里面有这个字符串的内容str,以及这个字符串的次数。也就是一开始统计次数是在hashmap里面统计的,然后把里面的元素取出来做成一个node[]数组,然后把这个node[]数组来排序。
代码:
public static HashMap<String, Node> map;
static class Node {
public int times;
public String str;
public Node(int times, String str) {
this.str = str;
this.times = times;
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();// 一共几个字符串
int k = sc.nextInt();// topk
sc.nextLine();
String[] strs = new String[n];
map = new HashMap<>();
for (int i = 0; i < n; i++) {
strs[i] = sc.nextLine();
if (!map.containsKey(strs[i])) {
Node node = new Node(1, strs[i]);
map.put(strs[i], node);
}else{
map.get(strs[i]).times++;
}
}
Node[] nodes=new Node[map.size()];
int index=0;
for(Entry<String,Node> entry:map.entrySet()){
nodes[index++]=entry.getValue();
}
Node[] nodecopy=new Node[nodes.length];
System.arraycopy(nodes, 0, nodecopy, 0, nodes.length);
Arrays.sort(nodes,new Comparator<Node>(){//次数大的放前面,次数相同就根据字典序排序
public int compare(Node node1,Node node2){
if(node1.times!=node2.times){
return node2.times-node1.times;
}else{
return node1.str.compareTo(node2.str);
}
}
});
Arrays.sort(nodecopy,new Comparator<Node>(){//次数小的放前面,次数相同就根据字典序排序
public int compare(Node node1,Node node2){
if(node1.times!=node2.times){
return node1.times-node2.times;
}else{
return node1.str.compareTo(node2.str);
}
}
});
for(int i=0;i<k;i++){
System.out.println(nodes[i].str+" "+nodes[i].times);
}
for(int i=0;i<k;i++){
System.out.println(nodecopy[i].str+" "+nodecopy[i].times);
}
}