关于使用Boost库实现不合并值的重叠区间高效查询的技术咨询
使用Boost库实现不合并值的重叠区间高效查询的技术咨询
嘿,这个需求我太懂了——用boost::icl::interval_map的时候,它那自动合并重叠区间值的特性确实会在这种场景下添乱,毕竟咱们就是要保留每一个原始的(区间,值)对,不能让它们被“吸收”或者合并掉。下面给你几个贴合Boost生态的解决方案,完全不用跳出Boost家族:
方案1:给Boost ICL换个“不合并”的值类型策略
其实你完全可以在Boost ICL的框架内解决问题,关键是绕过interval_map的自动合并逻辑:
- 别用基础类型或者支持
operator+=的聚合类型作为值,改用不会被合并的容器类型,比如把每个值包装在std::vector里,或者自定义一个简单的结构体,让它的operator+=只做追加操作,不做合并。 - 举个实际的代码例子,假设你要存
std::string类型的值:
这样插入重叠区间的新值时,#include <boost/icl/interval_map.hpp> #include <vector> #include <string> namespace icl = boost::icl; using Interval = icl::discrete_interval<int>; using ValueStore = std::vector<std::string>; // 自定义合并策略:只追加新值,不合并任何内容 struct AppendOnly { template<typename T> void operator()(T& lhs, const T& rhs) const { lhs.insert(lhs.end(), rhs.begin(), rhs.end()); } }; using MyIntervalMap = icl::interval_map<int, ValueStore, icl::partial_absorber, AppendOnly>;interval_map只会把新值追加到对应容器里,既不会合并区间,也不会丢失任何原始条目。查询时得到的就是所有和目标区间相交的容器,里面完整保留了所有独立的原始值。
方案2:用Boost.MultiIndex构建自定义索引
如果觉得ICL的合并逻辑还是太“重”,Boost.MultiIndex会是更灵活的选择——它能给一组(区间,值)条目建立高效的重叠查询索引:
- 先定义存储区间和值的结构体:
#include <boost/multi_index_container.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/member.hpp> #include <boost/icl/interval.hpp> namespace icl = boost::icl; namespace bmi = boost::multi_index; struct IntervalValue { icl::discrete_interval<int> interval; std::string value; }; - 给这个结构体建立双有序索引,分别基于区间的起点和终点,这样就能高效筛选出重叠的条目:
查询时只需要筛选出“起点≤目标区间终点”且“终点≥目标区间起点”的条目即可,Boost.MultiIndex的范围查询能快速完成这个操作,完全不会合并任何原始数据。using IntervalValueContainer = bmi::multi_index_container< IntervalValue, bmi::indexed_by< // 按区间起点排序的索引 bmi::ordered_non_unique< bmi::member<IntervalValue, icl::discrete_interval<int>, &IntervalValue::interval>, icl::less<icl::discrete_interval<int>> >, // 按区间终点排序的索引(用于重叠查询) bmi::ordered_non_unique< bmi::member<IntervalValue, icl::discrete_interval<int>, &IntervalValue::interval>, [](const auto& a, const auto& b) { return icl::upper(a) < icl::upper(b); } > > >;
方案3:别纠结,直接用Boost.Geometry的RTree
你提到用boost::geometry::index::rtree感觉大材小用,但其实1D场景下它的代码非常简洁,性能也拉满——RTree本来就是为高效空间重叠查询设计的,1D只是它的一个轻量化特例:
#include <boost/geometry.hpp> #include <boost/geometry/index/rtree.hpp> namespace bg = boost::geometry; namespace bgi = boost::geometry::index; // 用1D点对表示区间(起点,终点) using Point = bg::model::point<int, 1, bg::cs::cartesian>; using Box = bg::model::box<Point>; // 存储(区间,值)对 using Entry = std::pair<Box, std::string>; bgi::rtree<Entry, bgi::rstar<16>> rtree; // 插入示例:区间[10,20)对应值"foo" rtree.insert(std::make_pair(Box(Point(10), Point(20)), "foo")); // 查询所有和[15,25)重叠的条目 std::vector<Entry> results; rtree.query(bgi::intersects(Box(Point(15), Point(25))), std::back_inserter(results));
这段代码完全没有冗余感,而且RTree的查询效率在1D场景下是顶级的,真的不用有“大材小用”的心理负担。
总的来说,如果你想完全贴合ICL的生态就选方案1;要更灵活的索引逻辑就用Boost.MultiIndex;追求最快上手、最高性能的话,Boost.Geometry的RTree其实是最优解之一。
内容来源于stack exchange
相关产品推荐
相关产品推荐

