[LeetCode] 692. Top K Frequent Words

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 topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        Map map = new HashMap<>();
        for (String s : words) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else
                map.put(s, map.get(s) + 1);
        }
        List list = new ArrayList<>(map.keySet());
        Collections.sort(list, new Comparator() {
            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 topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        Map map = new HashMap<>();
        List result = new ArrayList<>();
        for (String s : words) {
            if (!map.containsKey(s))
                map.put(s, 1);
            else
                map.put(s, map.get(s) + 1);
        }
        PriorityQueue pq = new PriorityQueue(new Comparator() {
            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 topKFrequent(String[] words, int k) {
        if (words == null) throw new IllegalArgumentException("argument is null");
        int length = words.length;
        Map map = new HashMap<>();
        List[] array = new ArrayList[length + 1];
        List 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 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/

你可能感兴趣的:(LeetCode)