简体   繁体   English

C ++ std :: set与自定义的lower_bound

[英]C++ std::set with a custom lower_bound

How would I perform a find() or lower_bound() function on a std::set using a comparator function that is independent of its key such that it still runs in O(log N) time? 如何使用独立于其键的比较器函数在std::set上执行find()lower_bound()函数,使其仍在O(log N)时间运行?

Suppose I define a data type foo with two variables x and y and have a std::set<foo> that uses x as the key value. 假设我定义了具有两个变量xy的数据类型foo ,并具有一个将x作为键值的std::set<foo>

struct foo {
    int x, y;
    foo(int x, int y) : x(x), y(y) {}
};

struct xCompare {
    bool operator() (const foo& i, const foo& j) const {
        return i.x < j.x;
    }
};

// Within main()
std::set<foo, xCompare> fooSetX;

Is it possible to perform a binary search using lower_bound() or some other function that compares the values of y ? 是否可以使用lower_bound()或其他一些比较y值的函数来执行二进制搜索?

For the sake of this argument, assume that x and y are unique and independent of each other, and that given two foo variables foo1 and foo2 , if foo1.x < foo2.x , then foo1.y < foo2.y . 为了这个参数,假设xy是唯一且彼此独立的,并且给定两个foo变量foo1foo2 ,如果foo1.x < foo2.x ,则foo1.y < foo2.y This means that I cannot express y as a function of x , but is also ordered by y within fooSetX . 这意味着我不能将y表示为x的函数,但是在fooSetX也由y fooSetX

For example, given three foo(x,y) values (2,5), (3,9) and (5,10) within fooSet , a lower_bound() that takes y = 7 as the search term would return an iterator pointing to (3,9). 例如,给定fooSet三个foo(x,y)值(2,5),(3,9)和(5,10 lower_bound() ,将y = 7作为搜索项的lower_bound()将返回迭代器指向至(3,9)。

Currently, the way I go about solving this is by having two std::set<foo> s, ordered by x and y respectively. 目前,我要解决此问题的方法是拥有两个std::set<foo> ,分别按xy排序。 Whenever I need to search by y , I use the second std::set . 每当我需要按y搜索时,我都使用第二个std::set

struct yCompare {
    bool operator() (const foo& i, const foo& j) const {
        return i.y < j.y;
    }
};

// Within main()
std::set<foo, yCompare> fooSetY;

// Inserting elements
fooSetX.insert(foo(2,5)); fooSetY.insert(foo(2,5));
fooSetX.insert(foo(3,9)); fooSetY.insert(foo(3,9));
fooSetX.insert(foo(5,10)); fooSetY.insert(foo(5,10));

// lower_bound() with y = 7
std::set<foo>::iterator it = fooSetY.lower_bound(foo(0,7)); // points to (3,9)

You cannot directly pass a custom comparator to std::set::lower_bound - you need to pass it to the class template itself, as it will be internally used to maintain the order of the objects (consequently making std::set::lower_bound work) . 您不能直接将自定义比较器传递给std::set::lower_bound您需要将其传递给类模板本身,因为它将在内部用于维护对象的顺序(因此使std::set::lower_bound工作)

Here's how the std::set template is defined : 这是std::set模板的定义方式

template<
    class Key,
    class Compare = std::less<Key>,
    class Allocator = std::allocator<Key>
> class set;

Compare is the only ordering customization point that allows you to provide a function object that will compare your objects as desired in place of std::less<Key> . Compare唯一的排序自定义点,它允许您提供一个函数对象 ,该函数对象将根据需要代替std::less<Key>来比较您的对象。

There's no way of adding additional ordering predicates to an std::set . 无法将其他排序谓词添加到std::set


If you want an additional ordering on your objects that will allow you to achieve O(log N) lookups, you could use another ordered structure that is kept in sync with the original one. 如果您希望对对象进行其他排序以实现O(log N)查找,则可以使用与原始对象保持同步的另一有序结构。 A std::set of pointers to objects in the first set that uses a different comparator could work. 一个std::set指向第一个使用不同比较器的对象的指针集可以工作。 Example: 例:

class MySet
{
private:
    std::set<Item, Comparator0> _set0;
    std::set<decltype(_set0.begin()), Comparator1> _set1;

public:
    void insert(Item x) 
    {
        auto res = _set0.insert(x);
        assert(res.second);

        _set1.insert(res.first);
    }

    const auto& lookup0(Key0 x) { return _set0.lower_bound(x); }
    const auto& lookup1(Key1 x) { return *(_set1.lower_bound(x)); }
};

Not with std::set, as @Vittorio Romeo points out in his answer. 不使用std :: set,就像@Vittorio Romeo在他的答案中指出的那样。

There is a boost datastructure that can lookup by unrelated members, which you would define like 有一个可以由不相关成员查找的boost数据结构 ,您可以定义为

struct foo {
    int x, y;
    foo(int x, int y) : x(x), y(y) {}
};

// helpers
struct x_tag {}; 
struct y_tag {};

boost::multi_index_container<
    foo,
    indexed_by<
        ordered_unique<tag<x_tag>, boost::multi_index::member<foo, int, &foo::x>>, // std::less<int> applied to foo::x
        ordered_unique<tag<y_tag>, boost::multi_index::member<foo, int, &foo::y>> // std::less<int> applied to foo::y
    >
> fooSet;

int an_x, an_y;
// lookup by x
fooSet.get<x_tag>().find(an_x);
fooSet.get<y_tag>().find(an_y);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM