🚩传送门:牛客题目

题目

给定一个字符串数组,再给定整数 [NC]97. 字符串出现次数的 TopK 问题 - 图1 ,请返回出现次数前 [NC]97. 字符串出现次数的 TopK 问题 - 图2 名的字符串和对应的次数。返回的答案应该按字符串出现频率由高到低排序。如果不同的字符串有相同出现频率,按字典序排序。对于两个字符串,大小关系取决于两个字符串从左到右第一个不同字符的 ASCII 值的大小关系。字符仅包含数字和字母 。

比如“ah1x”小于“ahb”“231”<“32”

数据范围:字符串数满足 [NC]97. 字符串出现次数的 TopK 问题 - 图3,每个字符串长度 [NC]97. 字符串出现次数的 TopK 问题 - 图4[NC]97. 字符串出现次数的 TopK 问题 - 图5
要求:空间复杂度 [NC]97. 字符串出现次数的 TopK 问题 - 图6,时间复杂度 [NC]97. 字符串出现次数的 TopK 问题 - 图7

解题思路:哈希表+小根堆

题目要求找出出现次数前 k 的字符串,最为简单的就是直接遍历数组用 Map 统计每个字符串出现的次数,接着将 Map 放入 List 再降序排序输出前 k 的字符串。但是此方法的时间复杂度 [NC]97. 字符串出现次数的 TopK 问题 - 图8不符合题目要求,因此将 List 修改为 小根堆 。

算法过程:

  • 使用哈希统计每个字符串出现的次数;
  • 建立容量为 k 小根堆,遍历 map,将统计后满足条件的字符放入最小堆中 。

    条件:个数多的,个数相等放入字典排序靠前的

复杂度分析

时间复杂度: [NC]97. 字符串出现次数的 TopK 问题 - 图9 ,堆中 K 个元素的排序时间

空间复杂度:[NC]97. 字符串出现次数的 TopK 问题 - 图10 ,需要存储 nMap.Entry

对于 List 比较规则是:9X 、8Y 、7A 、7B

  1. 数值大的在前
  2. 数值相等,ASCII 字典排序小的在前

image.png

  1. // 先是按出现次数降序比较,相同则再按照字符ASCII码升序比较
  2. Collections.sort(list,
  3. (o1, o2) ->
  4. (o1.getValue().compareTo(o2.getValue()) ==
  5. 0 ? o1.getKey().compareTo(o2.getKey()) :
  6. o2.getValue().compareTo(o1.getValue()))
  7. );

对于 Heap 比较规则是:7B 、7A 、8Y 、9X

  1. 数值小的在前(小根堆,堆顶值小)
  2. 数值相等,ASCII 字典排序大的在前

image.png

  1. PriorityQueue<Map.Entry<String,Integer>> queue = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {
  2. @Override
  3. public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
  4. if (o1.getValue().equals(o2.getValue())){
  5. //字典序大的在前 所以 o2 比 o1
  6. return o2.getKey().compareTo(o1.getKey());
  7. }else {
  8. //数量小的在前所以 o1 - o2
  9. return o1.getValue() - o2.getValue();
  10. }
  11. }
  12. });

我的代码 [ 匿名内部类Comparator ]

  1. public class Solution {
  2. public String[][] topKstrings (String[] strings, int k) {
  3. if (k == 0) return new String[][]{};
  4. String[][] res = new String[k][2];
  5. TreeMap<String,Integer> map = new TreeMap<>();
  6. // 统计各个字符串出现的次数
  7. for (int i=0;i<strings.length;++i){
  8. String s = strings[i];
  9. if (!map.containsKey(s)) {
  10. map.put(s, 1);
  11. } else {
  12. map.put(s, map.get(s) + 1);
  13. }
  14. }
  15. PriorityQueue<Map.Entry<String,Integer>> queue = new PriorityQueue<>(new Comparator<Map.Entry<String, Integer>>() {
  16. @Override
  17. public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
  18. if (o1.getValue().equals(o2.getValue())){
  19. return o2.getKey().compareTo(o1.getKey());
  20. }else {
  21. return o1.getValue() - o2.getValue();
  22. }
  23. }
  24. });
  25. for(Map.Entry<String,Integer> entry:map.entrySet()){
  26. // 堆的元素个数小于k,则直接加入堆中
  27. if (queue.size() < k) {
  28. queue.offer(entry);
  29. } else{
  30. if(queue.peek().getValue()<entry.getValue()||(queue.peek().getValue()==entry.getValue()&&queue.peek().getKey().compareTo(entry.getKey())>0)){
  31. //如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
  32. queue.poll();
  33. queue.offer(entry);
  34. }
  35. }
  36. }
  37. // 返回topK
  38. for (int i = k-1; i >=0; --i) {
  39. Map.Entry<String,Integer> entry =(Map.Entry)queue.poll();
  40. res[i][0] = entry.getKey();
  41. res[i][1] = String.valueOf(entry.getValue());
  42. }
  43. return res;
  44. }
  45. }

我的代码[ 外部类Comparator ]

  1. public class Solution {
  2. //自定义能够实现升序的比较器
  3. class DescComparator implements Comparator<Map.Entry<String,Integer>>
  4. {
  5. @Override
  6. public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
  7. if (o1.getValue().equals(o2.getValue())){
  8. return o2.getKey().compareTo(o1.getKey());
  9. }else {
  10. return o1.getValue() - o2.getValue();
  11. }
  12. }
  13. }
  14. public String[][] topKstrings (String[] strings, int k) {
  15. if (k == 0) return new String[][]{};
  16. Comparator compa = new DescComparator();
  17. String[][] res = new String[k][2];
  18. TreeMap<String,Integer> map = new TreeMap<>();
  19. // 统计各个字符串出现的次数
  20. for (int i=0;i<strings.length;++i){
  21. String s = strings[i];
  22. if (!map.containsKey(s)) {
  23. map.put(s, 1);
  24. } else {
  25. map.put(s, map.get(s) + 1);
  26. }
  27. }
  28. PriorityQueue queue = new PriorityQueue(k,compa);
  29. for(Map.Entry<String,Integer> entry:map.entrySet()){
  30. // 堆的元素个数小于k,则直接加入堆中
  31. if (queue.size() < k) {
  32. queue.offer(entry);
  33. } else if (compa.compare(queue.peek(),entry) < 0) {
  34. //如果堆顶元素 < 新数,则删除堆顶,加入新数入堆
  35. queue.poll();
  36. queue.offer(entry);
  37. }
  38. }
  39. // 返回topK
  40. for (int i = k-1; i >=0; --i) {
  41. Map.Entry<String,Integer> entry =(Map.Entry)queue.poll();
  42. res[i][0] = entry.getKey();
  43. res[i][1] = String.valueOf(entry.getValue());
  44. }
  45. return res;
  46. }
  47. }