C++ std::set 使用自定义lower_bound

C++ std::set with a custom lower_bound

本文关键字:lower 自定义 bound std set C++      更新时间:2023-10-16

如何使用独立于其键的比较器函数在std::set上执行find()lower_bound()函数,使其仍然在O(log N)时间内运行?

假设我定义了一个数据类型foo,其中包含两个变量xy,并且有一个使用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;

是否可以使用lower_bound()或其他比较y值的函数执行二分搜索?

为了这个论证,假设xy是唯一的并且彼此独立,并且给定两个foo变量foo1foo2,如果foo1.x < foo2.x,则foo1.y < foo2.y。这意味着我不能将y表示为x的函数,而是在fooSetX内按y排序。

例如,给定fooSet内的三个foo(x,y)值(2,5),(3,9)和(5,10),一个以y = 7作为搜索词的lower_bound()将返回一个指向(3,9)的迭代器。

目前,我解决这个问题的方法是有两个std::set<foo>,分别按xy排序。每当我需要按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)

您不能直接将自定义比较器传递给std::set::lower_bound- 您需要将其传递给类模板本身,因为它将在内部用于维护对象的顺序(因此std::set::lower_bound工作)。

以下是定义std::set模板的方式:

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

Compare是唯一的排序自定义点,允许您提供一个函数对象,该函数对象将根据需要比较您的对象来代替std::less<Key>

无法向std::set添加其他排序谓词。


如果要对对象进行额外的排序,以便实现O(log N)查找,则可以使用与原始结构保持同步的另一个有序结构。使用不同比较器的第一组中指向对象的指针std::set可以工作。例:

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)); }
};

不是 std::set,正如@Vittorio罗密欧在他的回答中指出的那样。

有一个提升数据结构可以按不相关的成员查找,您可以像这样定义

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);