• [LintCode 550.] 最常使用的K个单词II


    LintCode 550. 最常使用的K个单词II

    难度困难题

    题目描述

    在实时数据流中找到最常使用的k个单词.
    实现TopK类中的三个方法:
    TopK(k), 构造方法
    add(word), 增加一个新单词
    topk(), 得到当前最常使用的k个单词.

    样例

    样例 1:

    输入:
    TopK(2)
    add("lint")
    add("code")
    add("code")
    topk()
    输出:["code", "lint"]
    解释:
    "code" 出现两次并且 "lint" 出现一次, 它们是出现最频繁的两个单词。
    样例 2:

    输入:
    TopK(1)
    add("aa")
    add("ab")
    topk()
    输出:["aa"]
    解释:
    "aa" 和 "ab" 出现 , 但是aa的字典序小于ab。
    注意事项
    如果两个单词有相同的使用频率, 按字典序排名.

    解题思路

    topK问题的第一反应是建堆。但是本题还有点特殊,比较贴近实用场景的地方在于:元素的频率是动态更新的。
    考虑以下方案:

    1. 维护一个hashmap,每次添加元素更新hashmap,每次查询topK的时候建堆。—— 查询开销太大,高频查询效率低。
    2. 维护hashmap和堆,每次添加元素同步更新hashmap和堆。—— 堆不控制size的话空间占用太大;控制的话对于TopK(2) add("a") add("b") add("a")这种情况得出的答案是["a", "a"],因为首先被弹出的是(1,"b")而不是(1,"a"),造成元素重复。
    3. 如果有一种堆,拥有一个increaseKey的API可以直接指定更新某一个元素的值就好了,就和斐波那契堆可以直接decreaseKey一样。
    4. 堆不行就试试直接把key和value打包成key来做,使用TreeSet,改成对key的操作就行了。

    参考代码

    struct comparator {
        bool operator () (const pair<int,string>& lhs, const pair<int,string>& rhs) {
            if (lhs.first != rhs.first) return lhs.first > rhs.first;
            return lhs.second < rhs.second;
        }
    };
    
    class TopK {
    public:
        /*
        * @param k: An integer
        */TopK(int k) {
            // do intialization if necessary
            this->k = k;
        }
    
        /*
         * @param word: A string
         * @return: nothing
         */
        void add(string &word) {
            // write your code here
            if (k <= 0) return;
    
            wc[word]++;
            if (wc[word] > 1) st.erase(make_pair(wc[word]-1, word));
            st.insert(make_pair(wc[word], word));
            while(st.size() > k) st.erase(--st.end());
        }
    
        /*
         * @return: the current top k frequent words.
         */
        vector<string> topk() {
            // write your code here
            if (k <= 0) return {};
    
            vector<string> res;
            set<pair<int, string>, comparator>::iterator it = st.begin();
            for (int i=0; i<k; i++) {
                if (it == st.end()) break;
                res.push_back((*it).second);
                it++;
            }
            return res;
        }
    private:
        unordered_map<string, int> wc;
        // priority_queue<pair<int, string>, vector<pair<int,string>>, comparator> q;
        set<pair<int, string>, comparator> st;
        int k;
    };
    

    或者直接使用 set<string>

    unordered_map<string, int> wc;
    struct comparator2 {
        bool operator () (const string& lhs, const string& rhs) {
            if (wc[lhs] != wc[rhs]) return wc[lhs] > wc[rhs];
            return lhs < rhs;
        }
    };
    
    class TopK {
    public:
        /*
        * @param k: An integer
        */TopK(int k) {
            // do intialization if necessary
            this->k = k;
        }
    
        /*
         * @param word: A string
         * @return: nothing
         */
        void add(string &word) {
            // write your code here
            if (k <= 0) return;
            // wc[word]++;
            if (st.find(word) != st.end()) st.erase(word);
            wc[word]++;
            st.insert(word);
            while(st.size() > k) st.erase(--st.end());
        }
    
        /*
         * @return: the current top k frequent words.
         */
        vector<string> topk() {
            // write your code here
            if (k <= 0) return {};
    
            return vector<string>(st.begin(), st.end());
        }
    private:
        // unordered_map<string, int> wc;
        // priority_queue<pair<int, string>, vector<pair<int,string>>, comparator> q;
        set<string,comparator2> st;
        int k;
    };
    
  • 相关阅读:
    告别08
    WinForm程序如何将子窗体嵌入到父窗体的Panel里
    几种排序算法
    接口的特征
    什么是重写
    C#异常处理
    什么是重载
    结构和类的区别
    Javascript的函数
    在ASP.NET中防止注入攻击
  • 原文地址:https://www.cnblogs.com/zhcpku/p/14286847.html
Copyright © 2020-2023  润新知