高效搜索范围列表

时间:2016-09-16 04:19:45

标签: c++ c++11 c++14

我有范围列表{start,end}和一个值(点),现在我正在寻找从给定值出现的范围中获取最后一个索引的有效方法。

例如: 清单:[{0,4},{5,10},{11,14},{15,20},{21,25}] n:2 价值:22

所以这里,22在范围{21,25}中,其在索引4(基于0)。 由于n为2,函数应返回{11,14}的索引,因为这是匹配范围的第n个范围。

在这里,我可以轻松地编写二进制函数,因为我已经对范围列表进行了排序。但我不想写/ for,我正在寻找一些C ++ 11/14算法/ lambdas(如果可用),这可以解决这个问题。

什么是有效的解决方案?

2 个答案:

答案 0 :(得分:3)

假设您的点存储为std::pair并且返回迭代器而不是索引是可以接受的:

template <typename container_t, typename value_t, typename n_t>
auto KailasFind(const container_t& vec, value_t value, n_t n) {
    auto match = std::find_if(vec.begin(), vec.end(), [&](const auto& p) {
        return value >= p.first && value <= p.second;
    });
    return match - n;
}

用法:

using point_t = std::pair<int, int>;
std::vector<point_t> vec {{0, 4}, {5, 10}, {11, 14}, {15, 20}, {21, 25}};
auto it_to_result = KailasFind(vec, 22, 2);
auto result = *it_to_result;

答案 1 :(得分:2)

我喜欢Jan的答案,但是如果您的数据 已知排序,那么相应的解决方案会有所不同,这里是问题的答案:

#include <cstddef>
#include <utility>
#include <stdexcept>
#include <algorithm>
#include <iterator>

template<typename RngT, typename ValT, typename OffsetT>
std::size_t find_prev_interval(RngT const& rng, ValT const& value, OffsetT const offset) {
    using std::begin; using std::end;
    auto const first = begin(rng), last = end(rng);
    auto const it = std::lower_bound(
        first, last, value,
        [](auto const& ivl, auto const& v) { return ivl.second < v; }
    );

    // optional if value is *known* to be present
    if (it == last || value < it->first) {
        throw std::runtime_error("no matching interval");
    }

    auto const i = std::distance(first, it);
    return offset <= i
      ? i - offset
      : throw std::runtime_error("offset exceeds index of value");
}

由于实现只需要forward-iterators,这适用于任何标准库容器或C-array;但对于std::set<>boost::containers::flat_set<>之类的内容,您希望更改逻辑以调用rng.lower_bound()而不是std::lower_bound()。另外,如果offset通常过大而无法返回有效索引,请将boost::optional替换为异常。