题解 | #字符串出现次数的TopK问题#

字符串出现次数的TopK问题

http://www.nowcoder.com/practice/fd711bdfa0e840b381d7e1b82183b3ee

方法一

思路

题目要求找出出现次数前k的字符串,最为简单的就是直接遍历数组统计每个字符串出现的次数,接着再降序排序输出前k的字符串。

具体步骤

  • 首先判断k值是否为0,若为0,则直接返回一个空的String二维数组;

  • k值大于0时,通过哈希计算每个字符串出现的次数;

  • 借助JDK的比较器Collection.sort,自定义降序排序规则,对统计结果进行降序排序;

  • 输出排序后的前k个字符串,以及其出现次数。

  • 代码如下:

    import java.util.*;
    public class Solution {
    /**
    * return topK string
    * @param strings string字符串一维数组 strings
    * @param k int整型 the k
    * @return string字符串二维数组
    */
    public String[][] topKstrings (String[] strings, int k) {
       // write code here
       if (k == 0){
           return new String[][]{};
       }
       String[][] res = new String[k][2];
       TreeMap<String,Integer> map = new TreeMap<>();
       // 统计各个字符串出现的次数
       for (int i=0;i<strings.length;++i){
           String s = strings[i];
           if (!map.containsKey(s)) {
               map.put(s, 1);
           } else {
               map.put(s, map.get(s) + 1);
           }
       }
    
       ArrayList<Map.Entry<String, Integer>> list = 
           new ArrayList<>(map.entrySet());
       // 先是按出现次数降序比较,相同则再按照字符ASCII码降序比较
       Collections.sort(list,
                        (o1, o2) -> 
                        (o1.getValue().compareTo(o2.getValue()) ==
                         0 ? o1.getKey().compareTo(o2.getKey()) : 
                         o2.getValue().compareTo(o1.getValue()))
                       );
       // 返回topK
       for (int i = 0; i < k; i++) {
           res[i][0] = list.get(i).getKey();
           res[i][1] = String.valueOf(list.get(i).getValue());
       }
       return res;
    }
    }
  • 时间复杂度:,遍历统计为,但是降序排序需要

  • 空间复杂度:,使用hashmap辅助结构。

    方法二

    思路

    方法一的时间复杂度为,而题目中要求时间复杂度为,虽然能够通过测试用例,但是其并不满足要求,而nlogk使我想到了堆这一数据结构,当将堆的大小维持在k时,对于n个数据进行排序,其时间复杂度不是正好为吗,所以本题可以考虑使用最小堆来实现(不使用最大堆是因为为了保证最后堆中存储的元素为topK)。

    具体步骤

  • 首先判断k是否为0,k为0,直接返回空字符数组;

  • 使用哈希统计每个字符串出现的次数;

  • 建立容量为k的最小堆,遍历map,将统计后的字符放入最小堆中,同时需要控制堆中的元素数量,具体过程可以看下图的例子:
    图片说明

  • 基于最小堆排完序后,再将topK输出到字符数组中,返回即可。

    import java.util.*;
    public class Solution {
    /**
    自定义能够实现升序的比较器
    */
    class DescComparator implements Comparator<Map.Entry<String,Integer>>
    {
       @Override
       public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
           if (o1.getValue().equals(o2.getValue())){
               //字典序小的在前 所以 o1 比 o2
               return o2.getKey().compareTo(o1.getKey());
           }else {
               //数量小的在前所以 o1 - o2
               return o1.getValue() - o2.getValue();
           }
       }
    }
    /**
    * return topK string
    * @param strings string字符串一维数组 strings
    * @param k int整型 the k
    * @return string字符串二维数组
    */
    public String[][] topKstrings (String[] strings, int k) {
       // write code here
       if (k == 0){
           return new String[][]{};
       }
       Comparator compa = new DescComparator();
       String[][] res = new String[k][2];
       TreeMap<String,Integer> map = new TreeMap<>();
       // 统计各个字符串出现的次数
       for (int i=0;i<strings.length;++i){
           String s = strings[i];
           if (!map.containsKey(s)) {
               map.put(s, 1);
           } else {
               map.put(s, map.get(s) + 1);
           }
       }
       PriorityQueue queue = new PriorityQueue(k,compa);
       for(Map.Entry<String,Integer> entry:map.entrySet()){
           // 堆的元素个数小于k,则直接加入堆中
           if (queue.size() < k) {
               queue.offer(entry);
           } else if (compa.compare(queue.peek(),entry) < 0) {
               //如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
               queue.poll();
               queue.offer(entry);
           }
       }
    
       // 返回topK
       for (int i = k-1; i >=0; --i) {
           Map.Entry<String,Integer> entry =(Map.Entry)queue.poll();
           res[i][0] = entry.getKey();
           res[i][1] = String.valueOf(entry.getValue());
       }
       return res;
    }
    }
  • 时间复杂度:,统计为,排序为

  • 空间复杂度:,统计需要

数据结构算法学习 文章被收录于专栏

个人算法学习,记录自己之前学过的东西,方便复习

全部评论
// 统计各个字符串出现的次数可以这样简写 for (String s:strings){ map.put(s,map.getOrDefault(s,0)+1); }
1 回复 分享
发布于 2023-03-06 14:46 陕西

相关推荐

04-08 23:14
已编辑
南阳理工学院 算法工程师
本人情况:26届双非本科,两段实习经历,目前拿到的都是实习的offer,一个校招的都没有,他们都说先实习,然后等拿到毕业证了直接转正,我又害怕干三个月给我叉出去面试题也发一下吧###&nbsp;杭州问尔信息技术后端登录你是怎么做的?JWT令牌你了解过吗?他虽然是一段字符串,他表达了什么东西?怎么解析出来信息和过期时间?JWT令牌怎么续期?如果我拉黑一个账号,要怎么做?两种方案(有无redis)mongodb和mysql的区别?mongodb和mysql分别实用于什么项目?选型你会怎么选?数据库的事务,那些地方需要使用,那些地方不需要使用?他会影响什么性能?mysql和pgsql有什么差异你知道吗?消息队列&nbsp;redis也有,为什么要用mq?前后端会部署吗?docker会用吗?内部通信前端&nbsp;async和&nbsp;await你知道吗?异步编程的原理是什么?vue3&nbsp;为什么你改变一个字符串&nbsp;前端会跟着改动AI工具会用什么?你会怎么用?###&nbsp;仲财通常用的锁有哪些synchronize和ReentrantLock的区别分布式锁了解吗?分布式事务mysql表字段sql优化什么时候用索引索引什么时候会失效mysql事务ioc一些项目应用问题###&nbsp;观妙科技项目问题...zset的架构是什么样子的线程池突然队列被打满了怎么办?如果上游和下游都无法控制,该怎么维护select&nbsp;*&nbsp;from&nbsp;user&nbsp;where&nbsp;age&gt;20&nbsp;order&nbsp;by&nbsp;update_time&nbsp;索引设计检索过程是什么样的冒泡排序和快排,有什么区别怎么判断链表有没有环###&nbsp;观妙科技-二面项目部分...线程池的核心参数有哪些你是怎么用线程池的JMMG1模型跳表介绍一下平衡二叉树TCP为什么要三次握手?说一下hashmap红黑树的特征你有什么学习的方法
牛马人的牛马人生:对学院本而言很强了
点赞 评论 收藏
分享
评论
22
5
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务