C++ 通过范围列表进行高效搜索

C++ 通过范围列表进行高效搜索,c++,c++11,c++14,C++,C++11,C++14,我有一个范围{start,end}的列表和一个值点,现在我正在寻找一种有效的方法,从给定值所在的范围中获取最后的第n个索引 例如: 清单:[{0,4},{5,10},{11,14},{15,20},{21,25}] n:2 价值:22 这里,22在{21,25}范围内,这个范围是以索引40为基础的。 由于n是2,函数应该返回{11,14}的索引,因为这是匹配范围的第n个范围 在这里,我可以很容易地编写二进制函数,因为我已经对范围列表进行了排序。但我不想写,我正在寻找一些C++ 11/14算法/l

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

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

这里,22在{21,25}范围内,这个范围是以索引40为基础的。 由于n是2,函数应该返回{11,14}的索引,因为这是匹配范围的第n个范围

在这里,我可以很容易地编写二进制函数,因为我已经对范围列表进行了排序。但我不想写,我正在寻找一些C++ 11/14算法/lambdas,如果可以的话,可以解决这个问题。
什么是有效的解决方案?

假设您的点存储为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;

假设您的点存储为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;

我喜欢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");
}

由于实现只需要前向迭代器,这将适用于任何标准库容器或C数组;但是,对于std::set或类似boost::containers::flat_set的东西,您需要更改逻辑以调用rng.lower_-bound,而不是std::lower_-bound。此外,如果偏移量通常太大而无法返回有效索引,请将异常替换为类似的内容。

我喜欢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");
}

由于实现只需要前向迭代器,这将适用于任何标准库容器或C数组;但是,对于std::set或类似boost::containers::flat_set的东西,您需要更改逻辑以调用rng.lower_-bound,而不是std::lower_-bound。另外,如果偏移量通常太大而无法返回有效的索引,则替换异常。

因此,如果值为17,则您将返回范围{5,10},即列表中的索引1???@JoachimPileborg是可能的重复。这些范围有序、不重叠且不留间隙是偶然的吗?如果不是,则会改变方法,应在问题中说明。因此,如果值为17,则返回范围{5,10},即列表中的索引1???@JoachimPileborg是可能的重复,这些范围是有序的、不重叠的,并且没有间隙,这是偶然的吗?如果不是,这就改变了方法,应该在问题中说明。排序列表中的线性搜索如何有效?@Henrik问题的作者没有指定容器保证被排序。这个答案的目的是尽可能广泛地适用。排序列表中的线性搜索如何有效?@Henrik,问题的作者没有指定容器保证被排序。这个答案旨在尽可能广泛地适用。