Java uses PriorityQueue to implement topk sorting

TopK thought:

https://blog.csdn.net/wufaliang003/article/details/82940218

Title description

Given an array strArr of String type, and then an integer k, please print out the top k strings in strict order of ranking.

[Claim]

If the length of strArr is N, the time complexity should reach O(N \log K)O(NlogK)

 

Output K lines, each line has a string and an integer (string representation).
You need to output in descending order of the number of occurrences. If the number of occurrences is the same, the string with the smaller dictionary order will be output first.
 

Example 1

enter

copy

["1","2","3","4"],2

return value

[["1","1"],["2","1"]]

Example 2

enter

["1","1","2","3"],2

return value

[["1","2"],["2","1"]]

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<String,Integer> map = new HashMap();
        for(String s : strings){
            //初始化 数组中每个字符串默认出现一次
            map.put(s,map.getOrDefault(s,0)+1);
        }
        
        // 优先队列,实现小顶堆,输出到结果集就是堆的逆序,即倒序输出小顶堆,即大顶堆
        //https://blog.csdn.net/wufaliang003/article/details/82940218参考最小堆的思想
        PriorityQueue<Node> minHeap = new PriorityQueue();
        for(Map.Entry<String,Integer> entrySet : map.entrySet()){
            Node node = new Node(entrySet.getKey(),entrySet.getValue());
            //先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
            if(minHeap.size() < k){
                minHeap.add(node);
            }else{
                // 堆中元素等于 k 个时
                // 当 node的值大于栈顶元素,或者值相同时node的字典小于栈顶元素 时(最小堆构建规则,就是如此)
                //这相当于上一个条件:先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
                //接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元素,并调整堆,
                //以保证堆内的k个元素,总是当前最大的k个元素。
                if(minHeap.peek().compareTo(node) < 0){
                    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<Node>{
        
        //对应上面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 < node.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);
            }
        }
    }
}

 

Guess you like

Origin blog.csdn.net/luzhensmart/article/details/112944307