【问题标题】:How to implement a map with limited size如何实现大小有限的地图
【发布时间】:2018-07-11 06:55:42
【问题描述】:

我想实现一个元素数量永远不会超过某个限制L 的地图。插入L+1-th 元素时,应从映射中删除最旧的条目以清空空间。

我发现了类似的东西:Data Structure for Queue using Map Implementations in Java with Size limit of 5。建议使用链接哈希映射,即哈希映射还保留所有元素的链接列表。不幸的是,那是针对 java 的,我需要一个针对 C++ 的解决方案。我在标准库和 boost 库中都找不到类似的东西。

同样的故事:Add and remove from MAP with limited size

这里给出了一个可能的 C++ 解决方案,但它没有解决我下面的问题:C++ how to mix a map with a circular buffer?

我会以与那里描述的非常相似的方式实现它。用于存储键值对的哈希映射和用于保存条目索引的键的链表或双端队列。要插入一个新值,我会将其添加到哈希映射及其索引末尾的键中;如果此时 has 的大小超过限制,我将弹出索引的第一个元素并从 has 中删除具有该键的条目。简单,与添加到哈希映射相同的复杂性。

删除条目需要对索引进行迭代以从那里删除键,这对于链表和双端队列都具有线性复杂性。 (双端队列也有一个缺点,即移除一个元素本身就具有线性复杂性。)所以看起来对这种数据结构的移除操作并没有保留复杂性,因为底层具有映射

问题是:是否有必要增加复杂性,或者有一些巧妙的方法来实现有限的地图数据结构,以便插入和移除都保持相同的复杂性?


糟糕,刚刚发布并立即意识到一些重要的事情。索引的大小也是有限的。如果这个限制是恒定的,那么迭代它的复杂度也可以被认为是恒定的。

好吧,这个限制给出了移除操作成本的上限。

如果限制非常高,人们可能仍然更喜欢不涉及对索引进行线性迭代的解决方案。

【问题讨论】:

  • 所以你只需要一个额外的数据结构,平均在恒定时间内将一个键映射到一个迭代器到链表中?
  • 我认为你可以这样做:using Age = int; 你有第一张地图:std::map<K, std::pair<Age,V>>(或unordered_map,你喜欢),还有第二张地图:std::map<Age, I> 其中@ 987654330@。年龄计数器初始化为 0,在插入元素时递增,并且元素使用该年龄。然后你有 O(log n) 的插入和删除
  • 听起来像是 boost::multi_index 的工作?
  • 每个容器都有权衡。您不能对所有操作都具有 o(1) 复杂性。如果有办法,那将是我们唯一需要的容器。

标签: c++ memory data-structures hashmap limit


【解决方案1】:

我仍然会使用关联容器来进行直接访问,并使用顺序容器来轻松删除旧项目。让我们看一下所需的访问方法:

  • 访问给定键的元素 => 好的,关联容器允许直接访问
  • 添加新的键值对
    • 如果地图未满,很容易:push_back 在序列容器上,并简单地添加到关联容器中
    • 如果地图已满,则必须执行上述操作,但必须删除最旧的元素 => 序列容器上的 front 将给出该元素,pop_fronterase 将删除它,前提是密钥包含在序列容器中
  • 删除由其键给出的元素 => 从关联容器中删除很简单,但只有 list 允许在恒定时间内删除元素只要你有一个迭代器。好消息是,在列表中删除或插入元素不会使指向其他元素的迭代器失效。

由于您没有对保持键排序提出任何要求,因此我将使用unordered_map 作为关联容器,将list 用于序列一。附加要求是list 必须包含键,并且unordered_map 必须包含指向list 中其对应元素的迭代器。该值可以在任一容器中。由于我假设主要访问将是直接访问,因此我会将值存储在地图中。

归结为:

  • list<K> 允许识别最旧的密钥
  • unordered_map<K, pair<V, list<K>::iterator>>

它使 key 的存储空间增加了一倍,并添加了一个额外的迭代器。但是键应该不会太大,list::iterator 通常只包含一个指针:这会改变少量内存以提高速度。

这应该足以提供恒定的时间

  • 密钥访问
  • 插入新项目
  • 项目的关键删除

【讨论】:

  • 另外一种方法怎么样:将数据保存在 deque> 中并将 K 映射到 unordered_map 中的数据。 V* 是 deque 中指向 V 的指针(不会被 deque 中的前后操作失效)?
  • 不,您需要列表项上的指针(迭代器)才能将其删除。正如我假设主要访问将是关键访问,您为该(主要)访问添加了间接访问。
【解决方案2】:

您可能想看看 Boost.MultiIndex MRU example

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-10
    • 1970-01-01
    • 2018-12-31
    相关资源
    最近更新 更多