【问题标题】:Does the C++ standard library have a set ordered by insertion order?C++ 标准库是否有按插入顺序排序的集合?
【发布时间】:2011-12-09 17:01:04
【问题描述】:

C++ 标准库是否有“有序集”数据结构?所谓有序集,我的意思是与普通的std::set 完全相同,但它会记住您将项目添加到其中的顺序。

如果不是,模拟一个的最佳方法是什么?我知道你可以做一些事情,比如有一组对,每对存储它添加的数字和实际值,但如果有更简单的解决方案,我不想跳过箍。

【问题讨论】:

  • 所以您希望能够以排序顺序和插入顺序快速访问它?
  • 您感兴趣的set 的其他特征是什么?独特性?去除效果?
  • @Jefromi 我想快速判断一个项目是否在集合中,并尽可能按插入顺序对其进行迭代。
  • 您所描述的是 exact 场景 Boost.MultiIndex 是为创建的(请参阅@KerrekSB 的答案)。
  • @Rob 唯一性和检查某物是否在集合中的速度。

标签: c++ stl set c++-standard-library


【解决方案1】:

没有单一的同质数据结构具有此属性,因为它要么是顺序的(即元素按插入顺序排列)要么是关联的(元素根据值按某种顺序排列)。

最好的、干净的方法可能是类似Boost.MultiIndex,它允许您在容器上添加多个索引或“视图”,因此您可以拥有顺序和有序索引。

【讨论】:

  • +1 推荐 Boost.MultiIndex -- 它确实是这类问题的最佳解决方案。
  • 您可以在初始化集合时使用 lambda 作为比较函数。他想要元素的唯一性,但要按插入顺序。我认为这可能是可行的?我不明白为什么会禁止这样做。
  • 也许使用向量和唯一算法来删除非唯一元素。还是这比其他方法慢?我猜搜索算法会比简单地存储索引要慢。
  • @dylan:是的,但这不会是一个容器。您可以使用数组和算法创建任何可以想象的行为(计算机内存只是一个大数组)...
【解决方案2】:

与其创建你正在使用的任何类型的 std::set,为什么不将对象的 std::pair 和一个在每次插入时递增的索引传递给它?

【讨论】:

  • 如果您在插入现有值时非常小心,并且如果您以某种方式制定了如何在恒定时间内找到最大索引的策略,那将有点工作。也许 std::map<T, unsigned int> 会稍微优雅一些​​,因为它的值类型已经是一对(并且你得到了可变的映射元素)。
  • @SethCarnegie:请注意,您仍然必须能够找到当前的最高指数,而这在恒定甚至对数时间内实际上是不可能的。
  • @Kerrek 为什么?如果你从 0(我是)开始,你可以使用 map::size 找到最大的索引对吗?
  • @SethCarnegie:是的,确实,你可以使用size(),我没想到!
  • Kerrek 的其他答案总体上仍然更好,因为 Boost.MultiIndex 将让您有效地访问按插入顺序索引的容器。就这个map<T, unsigned int> 而言,这是一个O(n) 操作来查找具有给定索引的元素。插入顺序迭代是O(n^2) 没有额外的内存,或者O(n log n)O(n) 额外的内存,如果你将整个批次复制到一个数组中并在迭代之前排序。对赛斯的目的来说可能没问题,但相当严格......
【解决方案3】:

不,它没有。

这样的容器可能需要两个不同的迭代器,一个以添加顺序定义的顺序进行迭代,另一个以通常的set 顺序进行迭代。标准库中没有这种东西。

模拟它的一个选项是拥有某种类型的set,除了您关心的实际数据之外,它还包含一个侵入式链表节点。将元素添加到set 后,将其附加到链表中。在从set 中删除元素之前,请将其从链表中删除。这保证没问题,因为指向 set 元素的指针不会因除删除该元素之外的任何操作而失效。

【讨论】:

  • 啊,太糟糕了。你建议做什么?
【解决方案4】:

我认为答案相当简单,将集合与另一个可迭代结构(例如队列)结合起来。如果你想按照元素插入的顺序迭代集合,先将元素推入队列,在最前面的元素上做你的工作,然后弹出,放入集合。

【讨论】:

    【解决方案5】:

    [免责声明:我已经对this question 给出了类似的答案]

    如果您可以使用 Boost,一个非常简单的解决方案是使用仅标头库 Boost.Bimap(双向映射)。

    考虑以下示例程序,它将按插入顺序显示一些虚拟条目 (try out here):

    #include <iostream>
    #include <string>
    #include <type_traits>
    #include <boost/bimap.hpp>
    
    using namespace std::string_literals;
    
    template <typename T>
    void insertByOrder(boost::bimap<T, size_t>& mymap, const T& element) {
      using pos = typename std::remove_reference<decltype(mymap)>::type::value_type;
      // We use size() as index, therefore indexing the elements with 0, 1, ...
      mymap.insert(pos(element, mymap.size()));
    }
    
    int main() {
      boost::bimap<std::string, size_t> mymap;
    
      insertByOrder(mymap, "stack"s);
      insertByOrder(mymap, "overflow"s);
    
      // Iterate over right map view (integers) in sorted order
      for (const auto& rit : mymap.right) {
        std::cout << rit.first << " -> " << rit.second << std::endl;
      }
    }
    

    insertByOrder() 中的时髦类型别名用于将元素插入到以下行中的 boost::bimap(请参阅参考文档)。

    【讨论】:

      【解决方案6】:

      是的,它被称为向量或列表(或数组)。只需附加到向量即可将元素添加到集合中。

      【讨论】:

      • 是的,但所有这些都允许冗余元素,这很可能是 OP 试图避免的。
      • pg1989 是正确的,我不想重复元素,也不想线性搜索数组来检查。
      猜你喜欢
      • 1970-01-01
      • 2019-12-02
      • 1970-01-01
      • 1970-01-01
      • 2019-09-17
      • 2021-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多