剑指offer 字符串出现次数的TopK问题(哈希相关的问题)

本文介绍了一种优化的算法,通过HashMap统计字符串出现次数,利用PriorityQueue实现时间复杂度为O(NlogK)的Top K 字符串和出现次数的查找,适用于字符串数组查询。作者提供了两种解决方案并对比了其性能。

描述

给定一个字符串数组,再给定整数k,请返回出现次数前k名的字符串和对应的次数。

返回的答案应该按字符串出现频率由高到低排序。如果不同的字符串有相同出现频率,按字典序排序。

对于两个字符串,大小关系取决于两个字符串从左到右第一个不同字符的 ASCII 值的大小关系。

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

字符仅包含数字和字母

[要求]

如果字符串数组长度为N,时间复杂度请达到O(NlogK)

第一种解法:现存入HashMap,此时时间复杂度为O(N),再将HashMap转为List进行排序,此时时间复杂度O(nlogn),最后取出k个元素,所以总的时间复杂度为O(nlogn)

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
        Map<String, Integer> map = new HashMap<>();
        for(int i=0;i<strings.length;i++){
            if(map.get(strings[i]) == null){
                map.put(strings[i], 1);
            }else{
                map.put(strings[i], map.get(strings[i]) + 1);
            }
        }
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        List<Map.Entry<String, Integer>> list = new ArrayList(set);
        Collections.sort(list, new MyCompare());
        String[][] res = new String[k][2];
        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;
    }
    
    class MyCompare implements Comparator<Map.Entry<String, Integer>>{
        
        @Override
        public int compare(Map.Entry<String, Integer> entry1, Map.Entry<String, Integer> entry2){
            int res = entry2.getValue().compareTo(entry1.getValue());
            if(res == 0){
                return entry1.getKey().compareTo(entry2.getKey());
            }
            return res;
        }
    }
}

可以使用lambda表达式进行优化

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
        Map<String, Integer> map = new HashMap<>();
        for(int i=0;i<strings.length;i++){
            if(map.get(strings[i]) == null){
                map.put(strings[i], 1);
            }else{
                map.put(strings[i], map.get(strings[i]) + 1);
            }
        }
        Set<Map.Entry<String, Integer>> set = map.entrySet();
        List<Map.Entry<String, Integer>> list = new ArrayList(set);
        Collections.sort(list, (entry1, entry2) -> {
            int res = entry2.getValue().compareTo(entry1.getValue());
            if(res == 0){
                return entry1.getKey().compareTo(entry2.getKey());
            }
            return res;
        });
        String[][] res = new String[k][2];
        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转为元素为Map.Entry的List。然后进行排序处理。但是这种方式时间复杂度是nlogn。

第二种解法,Topk的问题肯定会想到使用堆来解决,但是要注意如果是取Topk小值,此时使用大顶堆,如果是取Topk大值,此时使用小顶堆。这和直观上的理解有所区别。这里为了方便使用PriorityQueue来设计一个小顶堆。

代码实现:

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];
        Map<String, Integer> map = new HashMap<>();
        // 统计各个字符串出现的次数
        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);
            }
        }
        Comparator<Map.Entry<String, Integer>> comparator = (k1, k2) -> {
            Integer tmp = k1.getValue() - (k2.getValue());
            if (tmp.intValue() != 0) {
                return tmp;
            }
            return k2.getKey().compareTo(k1.getKey());
        };
        PriorityQueue<Map.Entry<String, Integer>> priorityQueue = new PriorityQueue<>(k, comparator);

        map.entrySet().forEach(entry -> {
            if (priorityQueue.size() < k) {
                priorityQueue.offer(entry);
            } else if (comparator.compare(priorityQueue.peek(), entry) < 0) {
                priorityQueue.poll();
                priorityQueue.offer(entry);
            }
        });
        String[][] result = new String[k][2];
        for (int j = k - 1; j >= 0; j--) {
            Map.Entry<String, Integer> element = priorityQueue.poll();
            result[j][0] = element.getKey();
            result[j][1] = element.getValue().toString();
        }
        return result;
    }
}

参考

字符串出现次数的TopK问题_牛客题霸_牛客网

堆结构的优秀实现类----PriorityQueue优先队列_Single_YAM的博客-CSDN博客_priorityqueue
 

Java使用PriorityQueue建立大小顶堆_学而不思则忘的博客-CSDN博客_java priorityqueue大顶堆小顶堆
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值