C++ std::set 自定义 lower_bound

C++ std::set with a custom lower_bound

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

假设我用两个变量 xy 定义了一个数据类型 foo 并且有一个 std::set<foo> 使用 x 作为键值。

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),一个 lower_bound() 需要 y = 7 作为搜索词 return 指向 (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 - 您需要将其传递给 class 模板 本身,因为它将在内部使用维护对象的顺序 (因此使 std::set::lower_bound 工作).

这是 std::set template is defined:

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

Compareonly 排序自定义点,它允许您提供一个 函数对象 来比较您的对象希望代替 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)); }
};

@Vittorio Romeo 在他的回答中指出,std::set 不是。

有一个boost datastructure可以被不相关的成员查找,你可以这样定义

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