【问题标题】:How to implement a queue of unique ids where elements can be "bumped" to the top in C++?如何实现一个唯一 id 队列,其中元素可以在 C++ 中“碰撞”到顶部?
【发布时间】:2020-05-12 08:14:44
【问题描述】:

我正在实现一个元素缓存(由唯一 ID 表示),其中包含要保留的最大元素数。当我达到最大尺寸时删除最后使用的元素。所以它看起来像一个队列,但具有独特的元素,因为我不想多次添加相同的 id。

但是元素可以多次使用,再次使用时应该回到队列顶部,这样缓存才真正删除最后使用的元素。

我真的不知道该怎么做。我的第一个猜测是使用 std::list,因此手动管理元素的唯一性和“移至顶部”操作。
有没有更聪明的方法来实现这一点?

编辑:我不知道名字,但这或多或少是Least recently used algorithms。

【问题讨论】:

    标签: c++ c++11 stl lru


    【解决方案1】:

    看起来这是一个简单的 LRU 缓存问题。这是一些有用的代码

    class LRUCache { 
    public:
    LRUCache(const LRUCache& other) = delete;
    LRUCache& operator=(LRUCache & other) = delete;       
    
    LRUCache(int capacity) {   
        size = capacity;
    }
    
    int get(int key) {
        auto it = mp.find(key);
        if(it != mp.end())
        {
            int val = it->second.first;
            use(it);
            return val;
        }
        return -1;
    }
    
    void put(int key, int value) {
        auto it = mp.find(key);
        if(it != mp.end())
        {
            it->second.first = value;
            use(it);
            return;
        }
    
        if(mp.size() == size)
        {
            // evict
            mp.erase(lst.back());
            lst.pop_back();
        }
        // add new
        lst.push_front(key);
        mp[key] = {value,lst.begin()};  
    }
    
    private:
    int size = 0;
    unordered_map<int,pair<int,list<int>::iterator>> mp;
    list<int> lst;
    
    void use(unordered_map<int,pair<int,list<int>::iterator>>::iterator& it)
    {
        lst.erase(it->second.second); // erase element from the list
        lst.push_front(it->first); // push key to front of the list
        it->second.second = lst.begin();
    }
    };
    

    【讨论】:

    • 听起来不错,谢谢。请注意,如果 元素 的副本很重,我们可以使用list::splice 来移动它而不是erase+push (stackoverflow.com/a/4912492/6165833)
    • splice 具有相同的复杂性。从给定指针的双向链表中擦除元素是 O(1),插入到头部也是 O(1)。我们在这里处理的只是指针,所以没有构造/破坏
    【解决方案2】:

    也许您正在寻找带有自定义比较运算符的std::set?

    我找到了一个多集here 的示例。 Multiset 允许重复,set 不允许,所以这更适合您的账单。

    借鉴同样的想法,你可以有一个这样的结构 -

    struct Elem
    {
        std::string UID;
        std::chrono::time_point lastUsed;
    }
    

    如果您重复使用缓存中的项目,请更改 lastUsed 参数。

    【讨论】:

    • 听起来是一个聪明而简洁的解决方案。但这意味着整个数据结构必须在每次更改/插入时重新排序,对吧?
    • 是的,在每次更改/插入/删除时都会重新排序,复杂度为 O(log n)。
    【解决方案3】:

    我们可以结合映射和双向链接来做到这一点。如果你的空间复杂度为 2 倍,那么使用映射,其中键是唯一 id,值是指向双向链表中节点的指针。双向链表将负责维护最后使用的 id。地图消除了重复的唯一 id。如果缓存大小达到限制,则找到双向链表中的第一个元素并从映射中删除该唯一 id 并删除列表的头元素。当使用唯一 id 时,然后在 map 中查找 id 的值并将节点移动到列表的开头。

    【讨论】:

      猜你喜欢
      • 2020-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-06
      • 1970-01-01
      • 2023-04-06
      相关资源
      最近更新 更多