【问题标题】:C++ std::set with a custom lower_boundC++ std::set 带有自定义的 lower_bound
【发布时间】:2017-03-17 11:42:02
【问题描述】:

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

假设我用两个变量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;

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

为了这个论证,假设xy是唯一的并且彼此独立,并且给定两个foo变量foo1foo2,如果foo1.x &lt; foo2.x,那么@ 987654338@。这意味着我不能将y 表达为x 的函数,而是在fooSetX 中由y 排序。

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

目前,我解决这个问题的方法是拥有两个std::set&lt;foo&gt;s,分别按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)

【问题讨论】:

    标签: c++ set stdset lower-bound


    【解决方案1】:

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

    std::set template is defined

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

    Compare唯一的排序自定义点,它允许您提供一个函数对象,该对象将根据需要比较您的对象,而不是std::less&lt;Key&gt;

    无法向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)); }
    };
    

    【讨论】:

    • 哦。那么在我的问题中提到的示例中,如何构造集合(我的意思是实际代码)?
    • @MuhammadIrhamRasyidi:哎呀,我误读了你的问题 - 你已经将比较器传递给std::set&lt;...&gt;...好吧,在调用@987654333 时,没有办法使用与yCompare 不同的比较器@.
    • 噢,伙计。我的一个想法是手动从根到叶遍历二叉搜索树,但我不知道该怎么做。
    • @MuhammadIrhamRasyidi:您可以简单地遍历每个值 for(const auto&amp; v : fooSetY) { if(v.x == 10) { /* ... */ } } - 这适用于您的用例吗?
    • 但是,这将在 O(N) 而不是 O(log N) 中运行。忘了在问题中提到 - 时间复杂度也是一个问题。
    【解决方案2】:

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

    【讨论】:

      猜你喜欢
      • 2015-10-27
      • 2011-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多