C++ 如何使用STL实现LFU缓存?

C++ 如何使用STL实现LFU缓存?,c++,algorithm,caching,stl,C++,Algorithm,Caching,Stl,我正在尝试使用纯STL实现LFU(使用频率最低的)缓存(我不想使用Boost!) 要求如下: 使用键与std::map类似的对任何元素的关联访问 能够释放最低优先级的项目(使用其UsesCount属性) 能够更新任何项目的优先级(UsesCount) 问题是: 如果我使用std::vector作为项的容器(Key,Value,UsesCount),std::map作为向量的迭代器容器,用于关联访问和std::make_heap,std::push_heap和std::pop_heap作为向

我正在尝试使用纯STL实现LFU(使用频率最低的)缓存(我不想使用Boost!)

要求如下:

  • 使用
    std::map
    类似的对任何元素的关联访问
  • 能够释放最低优先级的项目(使用其
    UsesCount
    属性)
  • 能够更新任何项目的优先级(
    UsesCount
问题是:


  • 如果我使用
    std::vector
    作为项的容器(
    Key
    Value
    UsesCount
    ),
    std::map
    作为向量的迭代器容器,用于关联访问和
    std::make_heap
    std::push_heap
    std::pop_heap
    作为向量内的优先级队列实现,堆操作后映射中的ITERTOR无效
  • 如果在以前的配置中使用
    std::list
    (或
    std::map
    )而不是
    std::vector
    std::make_heap
    等,则无法编译,因为它们的迭代器不支持算术
  • 如果我想使用
    std::priority\u queue
    ,我无法更新项目优先级
问题是:

  • 我是否遗漏了一些明显的问题,如何解决这个问题
  • 你能给我举一个LFU缓存的纯C++/STL实现来满足以前的需求吗

感谢您的见解。

您使用
*\u heap
函数和向量来实现似乎非常合适。尽管这会导致更新缓慢。对于使用向量作为底层数据结构的每个容器,遇到的迭代器失效问题是正常的。这也是所采用的方法,但由于上述原因,它没有提供可变接口。另一个提供了更新堆的能力

有些事情似乎有点奇怪:即使您能够使用
std::priority\u queue
,您仍将面临迭代器失效问题

直接回答你的问题:你没有遗漏明显的东西<代码>标准::优先级_队列没有它应有的用处。最好的方法是编写支持更新的堆实现。要使其完全兼容STL(特别是分配器感知)是相当棘手的,而不是一项简单的任务。在此基础上,实现LFU缓存

对于第一步,请查看Boost实现以了解工作情况。我不知道第二个的任何参考实现


要解决迭代器失效问题,您可以选择间接到另一个容器中,尽管您应该尽量避免它,因为它会产生额外的成本,并且会变得非常混乱。

这是一种比保留两个数据结构更简单的方法:

  • 只需保留一个映射,它将您的键映射到它们的值/使用计数对
  • 缓存已满时:
    • 创建映射元素的迭代器向量(
      O(n)
    • 使用
      std::nth_元素
      查找最差的10%(
      O(n)
    • 将它们全部从映射中删除(
      O(n日志n)
因此,向缓存中添加新元素是常见情况下的
O(logn)
,最坏情况下的
O(nlogn)
,以及摊销后的
O(logn)


在LFU缓存中删除最差的10%可能有点激烈,因为新条目必须达到前90%,否则将被删除。同样,如果只删除一个元素,那么在下一个新条目之前,新条目仍然需要从底部删除,否则它们会被剪切,这样做的时间就更少了。因此,取决于为什么LFU是适合您的正确缓存策略,我对它所做的更改可能是错误的策略,也可能仍然是正确的。

“堆操作后映射中的ITERTOR无效”-通过另一种方式解决此问题--将数据放在
映射中,即使插入/擦除了其他元素,也不会移动。然后将映射迭代器放入向量中,并用它构建一个堆。不过,您可能仍然无法有效地更新项目的优先级,因此这不是答案。谢谢您提供了我没有想到的另一个想法。但是如果我有
std::vector
std::map
迭代器,我如何定义它们的比较运算符,它将在
UsesCount
属性中查看指针对象的内部,要能够在项目插入后使用
std::make_heap
UsesCount
update来修复堆,请使用比较函数,如:
bool operator()(MapIter a,MapIterB){return a->second.UseCountsecond.UseCount;}
仅在迂腐的一面,您肯定不是指最常用(MFU)/最近使用最少的(LRU)?使用频率最低的,为什么要缓存这些?引用维基百科的表格“LFU计算一个项目的需要频率。使用频率最低的项目首先被丢弃。”,这意味着这正是我想要的。如何使用
*\u heap
更新优先级?我认为不仅仅是
优先级队列
不能在这里完成工作:标准堆函数不能。因此提问者需要一个不同的堆实现。尽管我可能错了。@Steveop可能我错了,但在pri更新之后ority再次调用
make_heap
应该可以修复堆。这可能远不是最佳的。同意。这将恢复堆不变量,但它是
O(n)
。其他堆实现可以在
O(log n)中增加/减少/更新
@SteveJessop我在答案中添加了一点,它变得更好了。@Blackhex:
make\u heap
可以在每次更新时调用。它所做的只是在给定的范围内对元素重新排序。插入时不需要调用它,因为
push\u heap
h