leetcode 460. LFU缓存

leetcode 460. LFU缓存

题目详情

题目链接
请你为 最不经常使用(LFU)缓存算法设计并实现数据结构。它应该支持以下操作:get 和 put。

  • get(key) - 如果键存在于缓存中,则获取键的值(总是正数),否则返回 -1。
  • put(key, value) - 如果键不存在,请设置或插入值。当缓存达到其容量时,则应该在插入新项之前,使最不经常使用的项无效。在此问题中,当存在平局(即两个或更多个键具有相同使用频率)时,应该去除 最近 最少使用的键。

「项的使用次数」就是自插入该项以来对其调用 get 和 put 函数的次数之和。使用次数会在对应项被移除后置为 0 。

  • 进阶:
    你是否可以在 O(1) 时间复杂度内执行两项操作?
  • 示例:
    LFUCache cache = new LFUCache( 2 /* capacity (缓存容量) */ );
    cache.put(1, 1);
    cache.put(2, 2);
    cache.get(1); // 返回 1
    cache.put(3, 3); // 去除 key 2
    cache.get(2); // 返回 -1 (未找到key 2)
    cache.get(3); // 返回 3
    cache.put(4, 4); // 去除 key 1
    cache.get(1); // 返回 -1 (未找到 key 1)
    cache.get(3); // 返回 3
    cache.get(4); // 返回 4

我的代码

class LFUCache {
public:

    list<pair<int, int>> useTimes;
    map<int, int> contents;
    int size;
    int cur;

    LFUCache(int capacity) {
        size = capacity;
        cur = 0;
    }

    inline void dealExist(int key) {
        pair<int, int> find;
        int findLoc = 0;
        bool isFind = false;
        for (auto loc = useTimes.begin(); loc != useTimes.end(); ++loc) {
            if (loc->first == key) {
                find = make_pair(key, loc->second + 1);
                useTimes.erase(loc);
                break;
            }
        }
        for (auto l = useTimes.begin(); l != useTimes.end(); ++l) {
            if (l->second > find.second) {
                useTimes.insert(l, find);
                isFind = true;
                break;
            }
        }
        if (!isFind) {
            useTimes.emplace_back(find);
        }
    }

    inline void insertFirst(int key) {
        bool isFind = false;
        for (auto l = useTimes.begin(); l != useTimes.end(); ++l) {
            if (l->second > 1) {
                useTimes.insert(l, make_pair(key, 1));
                isFind = true;
                break;
            }
        }
        if (!isFind) {
            useTimes.emplace_back(make_pair(key, 1));
        }
    }

    int get(int key) {
        if (size == 0) {
            return -1;
        }
        if (contents.count(key) == 0) {
            return -1;
        }
        dealExist(key);
        return contents[key];
    }

    void put(int key, int value) {
        if (size == 0) {
            return;
        }
        if (cur < size) {
            if (contents.count(key) == 0) {
                insertFirst(key);
                ++cur;
            } else {
                dealExist(key);
            }
        } else {
            if (contents.count(key) == 0) {
                contents.erase(useTimes.begin()->first);
                useTimes.erase(useTimes.begin());
                insertFirst(key);
            } else {
                dealExist(key);
            }
        }
        contents[key] = value;
    }
};

我的成绩

执行结果:通过
执行用时 : 1408 ms, 在所有 C++ 提交中击败了5.39%的用户
内存消耗 : 39.7 MB, 在所有 C++ 提交中击败了40.00%的用户

一些想法

  1. 本道题是一道困难题,但题目并不是很难,只是非常繁琐。
  2. 本题我的想法是用两个数据结构来记录内容,一个用 list<pair<int, int>> useTimes来记录key和使用次数,还有就是用 map<int, int> contents 来记录要保存的key和alue,然后在get和put时做相应更新即可。

执行用时为 80 ms 的范例

class LFUCache {
    class Bucket;
    class Key {
        public:
        int key, val; 
        Bucket *pfreq;

        Key *prev, *next;
        Key(int k, int v, Bucket* f) : key(k), val(v), pfreq(f) {}
        Key(Key* p, Key* n) : prev(p), next(n) {}
    };

    class Bucket {
        Key _head = Key(&_head, &_head);
        Key *head = &_head;

        public:
        int refc, sz = 0;
        Bucket() {}

        bool empty() {
            return sz == 0; 
        }

        void removeKey(Key* pkey) {
            pkey->prev->next = pkey->next;
            pkey->next->prev = pkey->prev;
            --sz;
        }

        void insertKey(Key* pkey) {
            pkey->next = head->next;
            pkey->prev = head;
            head->next->prev = pkey;
            head->next = pkey;
            ++sz;
        }

        int popLast() {
            Key *rm = head->prev;
            int key = rm->key;
            removeKey(rm);
            delete rm;
            return key;
        }
    };
    
    unordered_map<int, Bucket> blist;
    unordered_map<int, Key*> cache;

    int cap;
    Bucket* minf = NULL, *pzero = &blist[0];

    void add(int key, int v) {
        minf = pzero;
        Key* newKey = new Key(key, v, pzero);
        pzero->insertKey(newKey);
        cache.insert(make_pair(key, newKey));
    }

    int& val(int key) {
        Key* entry = cache[key];  
        Bucket *before = entry->pfreq, *after = &blist[before->refc + 1];
        after->refc = before->refc + 1;
        entry->pfreq = after;

        before->removeKey(entry);
        after->insertKey(entry);
        if(before->empty() && before == minf)
            minf = after;
        if(before->refc && before->empty())
            blist.erase(before->refc);
        
        return entry->val;
    }

    void pop() {
        int outkey = minf->popLast();
        cache.erase(outkey);
        if(minf->refc && minf->empty())
            blist.erase(minf->refc);
    }

public:
    LFUCache(int capacity) {
        cap = capacity;
        pzero->refc = 0;
    }

    int get(int key) {
        if(!cap || !cache.count(key)) 
            return -1;
        return val(key);
    }
    
    void put(int key, int value) {
        if(cap == 0) return;
        if(!cache.count(key)) {
            if(cache.size() == cap)
                pop();
            add(key, value);
        }
        else {
            val(key) = value;
        }
    }
};

思考

本道题推荐看官方讲解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值