【问题标题】:Replacing std::map with std::set and search by index用 std::set 替换 std::map 并按索引搜索
【发布时间】:2014-05-28 12:44:01
【问题描述】:

假设我们有一个map,其中包含更大的对象和一个索引值。索引值也是较大对象的一部分。

我想知道的是是否可以将map替换为set,提取索引值。

创建一个set 相当容易,它通过提取索引值对比较两个较大对象的仿函数进行排序。

这会留下按索引值搜索,我认为set 默认不支持。

我在考虑使用std::find_if,但我相信搜索是线性的,忽略了我们设置的事实。

然后我想到使用std::binary_search 和一个比较较大对象和值的函子,但我相信它在这种情况下不起作用,因为它不会利用结构并且会使用遍历没有随机访问迭代器。它是否正确?或者是否有重载可以正确处理set 上的这个调用?

最后我在考虑使用boost::containter::flat_set,因为它有一个底层向量,因此应该能够很好地与std::binary_search一起工作?

但也许有更简单的方法来做到这一点?

在您回答之前,只需在应该使用地图的地方使用地图 - 我实际上正在使用手动排序的矢量(好吧std::lower_bound)并且正在考虑用boost::containter::flat_set替换它,但它没有似乎很容易做到这一点,所以我可能会坚持使用矢量。

【问题讨论】:

  • 即使在阅读了你所有的问题之后,我也看不出你为什么不能使用 std::mapstd::unordered_map ...
  • @Cookie:看来你完全误解了 matts fud 传播咆哮。
  • std::setstd::map 只是彼此的变体:链接的 pdf 基本上同样适用于两者。核心问题是“你什么时候插入?与查找相比,你插入的频率是多少?当你迭代它时,元素的顺序是否重要?你需要容器​​的廉价副本吗?索引是对象身份的组成部分吗?有没有?元素多吗?boost过敏吗?你有不止一个指标吗?”

标签: c++ boost vector stl set


【解决方案1】:

C++14 将引入不需要构建整个存储对象的键来查找的能力。这可以按如下方式使用:

#include <set>
#include <iostream>

struct StringRef {
    StringRef(const std::string& s):x(&s[0]) { }
    StringRef(const char *s):x(s) { std::cout << "works: " << s << std::endl; }
    const char *x;    
};

struct Object {
    long long data;
    std::size_t index;
};
struct ObjectIndexer {
    ObjectIndexer(Object const& o) : index(o.index) {}
    ObjectIndexer(std::size_t index) : index(index) {}
    std::size_t index;
};
struct ObjComp {
    bool operator()(ObjectIndexer a, ObjectIndexer b) const { 
        return a.index < b.index; 
    }
    typedef void is_transparent; //Allows the comparison with non-Object types.
};

int main() {
    std::set<Object, ObjComp> stuff;
    stuff.insert(Object{135, 1});
    std::cout << stuff.find(ObjectIndexer(1))->data << "\n";
}

更一般地,使用Boost.MultiIndex 可以解决这些有多种索引数据方式的问题。

【讨论】:

    【解决方案2】:

    使用boost::intrusive::set,可以直接利用对象的索引值。它有一个具有对数复杂度的find(const KeyType &amp; key, KeyValueCompare comp) 函数。还有其他基于伸展树、AVL 树、替罪羊树等的集合类型,它们可能会根据您的要求表现更好。

    【讨论】:

      【解决方案3】:

      如果将以下内容添加到包含的对象类型中:

      • 小于仅比较对象索引的运算符
      • 仅比较对象索引的等式运算符
      • 一个构造函数,它采用您的索引类型并使用该索引值初始化一个虚拟对象

      然后您可以将您的索引类型传递给 find、lower_bound、equal_range 等...它会按照您想要的方式运行。当您将索引传递给集合(或 flat_set)的 find 方法时,它将构造一个包含类型的虚拟对象以用于比较。

      现在,如果您的对象真的很大,或者构建起来很昂贵,那么这可能不是您想要的方式。

      【讨论】:

        猜你喜欢
        • 2014-02-09
        • 1970-01-01
        • 2016-07-21
        • 1970-01-01
        • 2014-10-17
        • 2012-02-20
        • 2014-11-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多