【问题标题】:Simple and efficient container in C++ with characteristics of map and list containersC++中简单高效的容器,具有map和list容器的特点
【发布时间】:2010-11-01 15:55:26
【问题描述】:

我正在寻找一个可以同时享受地图容器和列表容器优势的 C++ 容器。

ma​​p 我想保持的容器优势:

  • O(log(n)) 次访问
  • operator[] 易用性
  • 稀疏性

列出我想保持的容器优势:

  • 项目之间有顺序
  • 能够轻松遍历列表更新:通过基于键或值的排序顺序

一个简单的示例应用程序是保存某些有效日期的列表(营业日期、假期、其他一些重要日期...),一旦给出特定日期,您可以立即找到它“地图样式”和然后找到下一个有效日期“列表样式”。

【问题讨论】:

  • STL 和 Boost 有什么我不理解的地方吗?
  • 在数据中保留索引怎么样?就像一个“日期”索引。这意味着多个容器指向同一个数据集。这会不利吗?
  • 当你说“在项目之间有一个订单”时,你的意思是有一个 任意 订单,定义为和何时添加(关于序列),还是您的意思是在键之间具有恒定关系的顺序(以关联容器的方式)?您不能将通过索引保证的快速访问与稀疏性、任意顺序结合起来(尽管您可以或多或少地使用多索引哈希表到达那里),所以当您说“O(1) " 访问,你的意思是 O(1) 通过键、索引还是两者都访问?
  • Map 有being()、end() 和find() 都返回允许顺序访问容器其余部分的迭代器。
  • @Jonathan:不正确:迭代器以正确的顺序遍历容器。请记住,地图是按键排序的。迭代器按照键定义的顺序遍历映射。如果你不喜欢默认的(std::less 默认使用operator <)排序顺序,你可以定义自己的。

标签: c++ map linked-list containers


【解决方案1】:

std::map 已经是一个排序容器,您可以在其中按顺序迭代包含的项目。不过,它只提供 O(log(n)) 访问权限。

std::tr1::unordered_map(或 C++0x 中的 std::unordered_map)具有 O(1) 访问权限,但未排序。

您真的需要 O(1) 访问权限吗?您必须使用大型数据集并为 O(log(n)) 进行 很多 次查找,速度不够快。

如果 O(log(n)) 足够了,std::map 会提供您所要求的一切。

【讨论】:

  • 你是对的 O(log(n))。我相应地改变了问题。
  • 我有一个涵盖两者的解决方案,但使用辅助容器。它一直在下降:-)
【解决方案2】:

如果不考虑稀疏性,可以看看Boost Multi-Index library。对于稀疏性质,您可以查看Boost Flyweight library,但我想您必须自己加入这两种方法。请注意,您的要求通常相互矛盾且难以实现。例如,O(1) 和项目之间的顺序很难有效维护。

【讨论】:

    【解决方案3】:

    地图通常以树的形式实现,因此具有对数查找时间,而不是 O(1),但听起来您想要一个排序的关联容器。哈希图有 O(1) 最好的情况,O(N) 最坏的情况,所以也许这就是你的意思,但它们没有排序,我认为它们还不是标准库的一部分。

    在 C++ 标准库中,mapsetmultimapmultiset 是排序的关联容器,但您必须放弃 O(1) 查找要求。

    【讨论】:

    • 你说得对 O(log(n))。我相应地改变了问题。
    【解决方案4】:

    根据 Stroustrup 的说法,地图的 [] 运算符为 O(log(n))。这比使用列表尝试这样的事情时得到的 O(n) 要好得多,但绝对不是 O(1)。唯一为[] 运算符提供此功能的容器是vector。

    除此之外,您已经可以使用地图来做所有的事情了。迭代器在它们上工作得很好。所以如果我是你,我会坚持使用地图。

    【讨论】:

    • 你说得对 O(log(n))。我相应地改变了问题。
    • 哈希表也可以为 operator[] 提供 O(1)。
    • @DeadMG - 仅在一定程度上。在最坏的情况下(所有冲突)哈希表恢复到与线性搜索相同的时间行为。如果您提前非常清楚自己的输入并为该输入提供完美(或接近完美)的哈希值,它们真的只会给您带来巨大的好处。
    • @T.E.D.对于绝大多数潜在输入,散列函数几乎没有冲突。不仅如此,Boost 和 TR1/C++0x 中预先提供了原始类型和 STL 容器的哈希值,因此用户不必编写自己的哈希值。
    • @DeadMG - 我明白你在说什么......我不反对。然而,大 O 表示法是一个理论概念,它有效地询问“当我们的输入大小接近无穷大时会发生什么”。由于散列始终是有限的,因此当在它们上使用大 O 表示法时,散列都会恢复到它们的冲突时间行为。对于较小的输入,它们当然更好。但是对于较小的输入,BubbleSort 也可以是一种相对快速的算法。 Big-O 不会谈论这些事情。
    【解决方案5】:
    • 项目之间有顺序
    • 能够轻松遍历列表

    地图已经做到了这两点。它们是排序的,所以你从 begin() 开始并遍历直到你到达 end()。当然,您可以从任何地图迭代器开始;您可能会发现 map 的 find、lower_bound 和相关方法很有帮助。

    【讨论】:

      【解决方案6】:

      您可以将数据存储在列表中,并映射到列表的迭代器,使您能够找到实际的列表元素本身。这种东西是我经常用于 LRU 容器的东西,我想要一个列表,因为我需要将访问的元素移动到末尾以使其成为最近访问的元素。您可以使用 splice 函数来执行此操作,并且从 2003 年标准开始,只要将迭代器保留在同一个列表中,它就不会使迭代器无效。

      【讨论】:

      • 这是我最初的想法。但我真的希望有一些我不知道的内置容器......
      【解决方案7】:

      这个怎么样:所有日期都存储在std::list<Date> 中,但是您可以使用辅助结构stdext::hash_map<Date, std::list<Date>::iterator> 来查找它。一旦有了列表的迭代器,访问下一个元素就很简单了。在您的 STL 实现中,它可能是 std::tr1::unordered_map 而不是 stdext::hash_map,并且还有 boost::unordered_map

      【讨论】:

      • 缺点是每次修改list都要重建hash_map。如果只是一个比较少的静态查找表也没关系,但这取决于容器的实际使用情况。
      • 我不认为应该重建整个hash_map。每个迭代器在相应元素存在于列表中的整个过程中都是有效的。同步是单次插入/删除。
      【解决方案8】:

      您永远找不到同时满足O(log n) 访问和有序性质的容器。原因是如果一个容器是有序的,那么它本质上必须支持任意顺序。这就是有序性质的含义:您可以准确决定任何元素的位置。因此,要找到任何元素,您必须猜测它在哪里。它可以在任何地方,因为您可以将它放在任何地方!

      请注意,有序序列与排序序列不同。排序的性质意味着任何两个元素之间存在一种特定的排序关系。有序性意味着元素之间可能存在多个排序关系。

      【讨论】:

      • 这是一个很好的区别。我需要一个有序序列,尽管有序序列特别是一个有序序列,所以它也可以。
      猜你喜欢
      • 2011-09-21
      • 1970-01-01
      • 1970-01-01
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 2019-07-05
      • 1970-01-01
      • 2022-01-16
      相关资源
      最近更新 更多