*692. Top K Frequent Words

Borris發表於2019-10-29

解法一 Override Collections.sort()

思路

We can use a HashMap to count the frequnecy of each word, and then add then into a list.
When sorting the list, override the sorting rules according to requirements. We sort the list by descending order, and if two Strings has equal frequency, the lower alphabetical order comes first.
After sorting, we take a sublist of the sorted list.

程式碼
class Solution {
    public List<String> topKFrequent(String[] words, int k) {
        List<String> res = new ArrayList<>();
        if (words.length == 0 || words == null) return res;

        HashMap<String, Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }

        Set<Map.Entry<String, Integer>> set = map.entrySet();

        for (Map.Entry<String, Integer> s : set) {
            res.add(s.getKey());
        }

        Collections.sort(res, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                if (map.get(a) == map.get(b)) {
                    return a.compareTo(b);
                }
                return map.get(b) - map.get(a);
            }
        });

        return res.subList(0, k);
    }
}
複雜度分析

時間複雜度
Counting the frequence for each word costs O(n), adding the word to list costs O(n), and sort the list costs O(nlogn).
空間複雜度
O(n) because we use a HashMap to count the frequency.

解法二

思路

Use the HashMap to count frequency for each word. Initialize a priority queue to sort the top k words. Override the rules for sorting - Worst frequent word at the top, if frequency is the same, the word with higher alphabetical comes first. So we can always poll the first element to keep the pq with size k.
Use a LinekdList to store the result. Always add the polled top element to the head of LinkedList.

程式碼
class Solution {
    public List<String> topKFrequent (String[] words, int k) {
        HashMap<String, Integer> map = new HashMap<>();
        for (String word : words) {
            map.put(word, map.getOrDefault(word, 0) + 1);
        }
        PriorityQueue<String> pq = new PriorityQueue<>((a, b) -> map.get(a).equals(map.get(b)) ? b.compareTo(a) : map.get(a) - map.get(b));

        for (String word : map.keySet()) {
            pq.offer(word);
            if (pq.size() > k) {
                pq.poll();
            }
        }

        List<String> res = new LinkedList<>();
        while(!pq.isEmpty()) {
            res.add(0, pq.poll());
        }

        return res;
    }
}
複雜度分析

時間複雜度
O(nlogk), because when add elements to pq, it takes O(logk) and we add words n times.
空間複雜度
O(n)

Takeaway

  • 重新複習了 Override Comparator 的幾種方法, 對於 PriorityQueue, override 其中的排序規則;對於 List, Override Collections.sort() 的排序規則。
  • List<Stirng> list = new LinkedList 時,由於多型,LinkedList 繼承 List 全部方法,而沒有專屬 LinkedList 的方法。所以使用 list.add(0, element) 來將元素新增進頭部。
  • 兩個方法都對 sorting 規則進行了重寫,但是第一種方法使用遞減因為我們之間擷取 list 即可,第二種方法使用遞增因為 PriorityQueue 的性質,我們每次都對頭部元素刪除保證 pq 的 size 為 k,勢必只能刪除 least frequent element。 為保證結果為正常順序,每次將 poll() 的元素加入 list 頭部即可。
  • list.subList(0, k) -- 擷取 list 的 [0, k) 元素形成新的 list。

相關文章