LeetCode每日一题 2021/5/20 692. 前K个高频单词

本篇博客介绍了如何解决LeetCode中的692题,即找出列表中出现频率最高的前K个单词。提供了两种解决方案,分别是利用哈希表加排序以及哈希表加优先级队列(小根堆)。对于每种方法,详细阐述了思路,并给出了Java代码实现,同时还分析了时间复杂度和空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

给一非空的单词列表,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字母顺序排序。

示例1

输入: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i" 在 "love" 之前。

示例2

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
    出现次数依次为 4, 3, 2 和 1 次。

方法一:哈希表+排序

思路

这一题的思路是先用哈希表算出列表中每个单词(字符串类型)出现的频数,然后对单词进行排序,如果两个单词的频数相同,那么让字母顺序小的单词排在前面,如果两个单词的频数不同,那么让频数大的排在前面

Java代码

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        Map<String, Integer> map = new HashMap<>();
        for(String word:words) {                                      // 通过哈希表,保存每个单词出现的频数
            map.put(word, map.getOrDefault(word, 0)+1);
        } 
        List<String> res = new ArrayList<>();                         // 定义一个由去重后的单词组成的List
        for(Map.Entry<String, Integer> entry : map.entrySet()) {
            res.add(entry.getKey());
        }
        Collections.sort(res, new Comparator<String>() {             
            public int compare(String s1, String s2) {
                if (map.get(s1) == map.get(s2)) {                     // 如果两个单词的频数相同,那么让字母顺序小的单词排在前面
                	return s1.compareTo(s2);
                } else {                                              // 如果两个单词的频数不同,那么让频数大的排在前面
                	return map.get(s2) - map.get(s1);
                }
            }
        });
        return res.subList(0, k);                                     // 返回前 k 个出现次数最多的单词
    }
}

时间复杂度:O(nlogk)。使用哈希表统计词频,复杂度为 O(n);排序的时间复杂度为O(nlogn)

空间复杂度:O(n)

方法二:哈希表+优先级队列(小根堆)

思路

通常在问题中看到 前 K 大前 K 小 或者 第 K 个K 个最 等等类似字样,一般情况下我们都可以用堆来做。先用哈希表算出列表中每个单词(字符串类型)出现的频数,然后因为题目要求前 K 大,所以构建一个 大小为 K 的小根堆按照上述规则自定义排序的比较器。然后依次将单词加入堆中(通过优先级队列实现),当堆中的单词个数超过 K 个后,我们需要弹出顶部最小的元素(顶部最小的元素是指当前频数最小的单词)使得堆中始终保留 K 个元素,遍历完成后剩余的 K 个元素就是前 K 大的。最后我们依次弹出堆中的 K 个元素加入到所求的结果集合中。由于此时结果集合中的元素是按照频数从低到高排序的,所以最后需要对集合进行翻转。

Java代码

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        Map<String, Integer> map = new HashMap<>();
        for(String word:words) {                                    // 通过哈希表,保存每个单词出现的频数
            map.put(word, map.getOrDefault(word, 0)+1);
        }
        PriorityQueue<String> pq = new PriorityQueue<String>(new Comparator<String>() {   
            public int compare(String s1, String s2) {              // 重点!!!注意,跟上面哈希表+排列相比,此处的compare()刚好相反,因为我们使用的是小根堆,每次弹出的顶部元素是频数最小的元素,这样最终保存下来的就是频数大的元素
                if(map.get(s1) == map.get(s2)) {
                    return s2.compareTo(s1);
                } else {
                    return map.get(s1) - map.get(s2);
                }
            }
        });
        for(String key : map.keySet()) {           // 依次将单词加入堆中(通过优先级队列实现)    
            pq.offer(key);
            if(pq.size() > k) {                    // 当堆中的单词个数超过 K 个后,我们需要弹出顶部最小的元素(顶部最小的元素是指当前频数最小的单词)使得堆中始终保留 K 个元素,遍历完成后剩余的 K 个元素就是频数前 K 大的    
                pq.poll();
            }
        }
        List<String> res = new ArrayList<>();
        while(pq.size() > 0) {
            String cur = pq.poll();
            res.add(cur);
        }                                         // 注意此时结果集合中的单词是按照频数从低到高排列的,所以后面还需要加上一步翻转
        Collections.reverse(res);
        return res;
    }
}

复杂度分析

时间复杂度:O(nlogk)使用哈希表统计词频,复杂度为 O(n);使用最多 n 个元素维护一个大小为 k 的堆,复杂度为 O(nlogk);输出答 案复杂度为 O(k)(同时 k≤n)。整体复杂度为 O(nlogk)

空间复杂度:O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值