[LeetCode] 692. Top K Frequent Words

本文介绍了一种高效算法,用于从给定的字符串数组中找出出现频率最高的k个字符串,并按频率从高到低排序,若频率相同则按字典序排序。提供了三种实现思路:使用排序的列表、维护一个最小堆以及利用频率数组配合排序。

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

Given a non-empty list of words, return the k most frequent elements.

Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first.

Example 1:

Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2
Output: ["i", "love"]
Explanation: "i" and "love" are the two most frequent words.
    Note that "i" comes before "love" due to a lower alphabetical order.

Example 2:

Input: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
Output: ["the", "is", "sunny", "day"]
Explanation: "the", "is", "sunny" and "day" are the four most frequent words,
    with the number of occurrence being 4, 3, 2 and 1 respectively.

Note:

  1. You may assume k is always valid, 1 ≤ k ≤ number of unique elements.
  2. Input words contain only lowercase letters.

Follow up:

  1. Try to solve it in O(n log k) time and O(n) extra space.

题目(本题与第347题如出一辙):给定一个非空字符串数组和一个目标值k,返回出现频率排在前k个的字符串(按照频率从大到小的顺序排列,如果频率相同则按首字母从小到大的顺序排列)。假设数组中的每个字符串只含有小写字母,且目标值k满足1<=k<=数组中不重复出现的元素总数。能否用时间复杂度是O(nlogk)、空间复杂度是O(n)的算法解决问题?

实现思路1:用HashMap统计每个字符串出现的频率,将所有不重复的字符串存进List并按字符串出现频率降序排序(如果频率相同则按字符串升序排序),最后取出List的前k个字符串就是结果。这种思路的时间复杂度是O(nlogn)、空间复杂度是O(n)。

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        Map<String, Integer> map = new HashMap<>();
        for (String s : words) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else
                map.put(s, map.get(s) + 1);
        }
        List<String> list = new ArrayList<>(map.keySet());
        Collections.sort(list, new Comparator<String>() {
            public int compare(String s1, String s2) {
                int freq1 = map.get(s1);
                int freq2 = map.get(s2);
                if (freq1 == freq2)
                    return s1.compareTo(s2);
                else
                    return freq2 - freq1;
            }
        });
        return list.subList(0, k);
    }
}

实现思路2:维护一个容量为k的堆,堆的排序规则是按字符串出现频率升序排序(如果频率相同则按字符串降序排序)。具体来说,用HashMap统计每个字符串出现的频率,然后逐个将字符串加入堆中,当堆的容量大于k时,删除堆首字符串(当前出现频率最小的字符串)。遍历完所有字符串后,逐个取出堆首字符串并存进一个List,最后这个List的逆序就是题目结果。由于堆中始终只有k个字符串,因此这种思路的时间复杂度是O(nlogk)、空间复杂度是O(n)。

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        Map<String, Integer> map = new HashMap<>();
        List<String> result = new ArrayList<>();
        for (String s : words) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else
                map.put(s, map.get(s) + 1);
        }
        PriorityQueue<String> pq = new PriorityQueue<String>(new Comparator<String>() {
            public int compare(String s1, String s2) {
                int freq1 = map.get(s1);
                int freq2 = map.get(s2);
                if (freq1 == freq2)
                    return s2.compareTo(s1);
                else
                    return freq1 - freq2;
            }
        });
        for (String key : map.keySet()) {
            pq.offer(key);
            if (pq.size() > k)
                pq.poll();
        }
        while (!pq.isEmpty())
            result.add(pq.poll());
        Collections.reverse(result);
        return result;
    }
}

实现思路3:借鉴第347题的求解思路,即长度为N的字符串数组中,每个字符最多出现N次、最少出现1次。因此创建并维护一个长度为N+1的数组A,A[i]是原数组中出现频率为i的字符串集合。具体来说,先用HashMap求出每个字符串的出现频率,然后将每个字符串按照其出现频率存进数组A中。按照题目要求,频率为i的所有字符串存进A[i]时按字符串升序排序。最后从数组A的末端遍历A非null集合,将集合中的字符串存进一个最终结果List,当List容量为k时就得到最终结果。这种思路的时间复杂度是O(nlogn),空间复杂度是O(n)。

class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        int length = words.length;
        Map<String, Integer> map = new HashMap<>();
        List<String>[] array = new ArrayList[length + 1];
        List<String> result = new ArrayList<>();
        for (String s : words) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else
                map.put(s, map.get(s) + 1);
        }
        for (String s : map.keySet()) {
            int freq = map.get(s);
            if (array[freq] == null)
                array[freq] = new ArrayList<>();
            array[freq].add(s);
        }
        for (int freq = length; freq >= 1; freq--) {
            List<String> temp = array[freq];
            if (temp == null) continue;
            Collections.sort(temp);
            int size = result.size();
            if (temp.size() >= k - size) {
                result.addAll(temp.subList(0, k - size));
                break;
            }
            else
                result.addAll(temp);
        }
        return result;
    }
}

参考资料:

https://leetcode.com/problems/top-k-frequent-words/solution/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值