哈希操作篇 -- 字符串出现次数的TopK问题

题目描述

牛客网:NC97 字符串出现次数的TopK问题
给定一个字符串数组,再给定整数k,请返回出现次数前k名的字符串和对应的次数。
返回的答案应该按字符串出现频率由高到低排序。如果不同的字符串有相同出现频率,按字典序排序。
对于两个字符串,大小关系取决于两个字符串从左到右第一个不同字符的 ASCII 值的大小关系。
比如"ah1x"小于"ahb",“231”<”32“
字符仅包含数字和字母
要求
如果字符串数组长度为N,时间复杂度请达到O(NlogK)

题目解析

  • 根据字符串出现的次数排序,首先需要遍历数组,获取每一个字符串的出现次数,用哈希表结构,key为字符串,value为出现的次数
  • 如果出现品类相同,按字典序排序,即按字符串自然顺序排序。有两种排序方式,因此可以用优先队列构建堆。

方法一:最大堆

  • 构建最大堆,首先需要构建比较器:若出现频率相同,则返回按字典序排在最前面的字符串,否则返回出现频率最高的字符串。
  • 确定出现次数:遍历一遍字符串数组,获取每个字符串的出现次数,然后插入堆中
  • 获取返回值:依次弹出k次堆顶元素,即为所需返回值。
public String[][] topKstrings (String[] strings, int k) {
        if(k == 0){
            return new String[][]{};
        }
        // 存放字符串出现次数
        Map<String,Integer> map = new HashMap<>();
        // 构建最大堆
        PriorityQueue<Map.Entry<String,Integer>> pq = new PriorityQueue<>(
            (o1,o2) -> o1.getValue().equals(o2.getValue()) ? o1.getKey().compareTo(o2.getKey()) : o2.getValue() - o1.getValue());
        // 初始化哈希表
        for(String str : strings){
            map.put(str,map.getOrDefault(str,0) + 1);
        }
        // 将哈希表内容放入堆中
        for(Map.Entry<String,Integer> entry : map.entrySet()){
            pq.offer(entry);
        }
        // 获取前k名字符串和出现次数
        String[][] ans = new String[k][2];
        for(int i = 0;i < k && !pq.isEmpty();i++){
            Map.Entry<String,Integer> entry = pq.poll();
            ans[i][0] = entry.getKey();
            ans[i][1] = String.valueOf(entry.getValue());
        }
        return ans;
    }

时间复杂度:O(nlogn),遍历统计为O(n),排序为O(nlogn)
空间复杂度:O(n)

方法二:最小堆

最大堆把所有字符串加入堆中,弹出前k个元素。对于指定返回值大小的情况,也可以用最小堆

  • 用哈希表存储字符串出现次数
  • 构建最小堆,大小为k
  • 将哈希表内容依次放入堆中。最小堆中堆顶元素时最小的,若当前元素大于堆顶元素,则弹出堆顶元素,从而更新堆内最小值。
  • 遍历结束后,堆中的元素即为前k名。
public String[][] topKstrings (String[] strings, int k) {
        if(k == 0){
            return new String[][]{};
        }
        Map<String,Integer> map = new HashMap<>();
        for(String str : strings){
            map.put(str,map.getOrDefault(str,0) + 1);
        }
        // 构建最小堆
        PriorityQueue<String> pq = new PriorityQueue<>(
            (o1,o2) -> map.get(o1).equals(map.get(o2)) ? o2.compareTo(o1) : map.get(o1) - map.get(o2)); 
        for(String key : map.keySet()){
            pq.offer(key);
            // 堆的大小超过k,弹出最小的
            if(pq.size() > k){
                pq.poll();
            }
        }
        String[][] ans = new String[k][2];
        for(int i = k - 1;i >= 0 && !pq.isEmpty();i--){
            String str = pq.poll();
            ans[i][0] = str;
            ans[i][1] = String.valueOf(map.get(str));
        }
        return ans;
    }

时间复杂度:O(nlogk),遍历统计为O(n),排序为O(nlogk)
空间复杂度:O(n)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值