我怎样才能插入到一个集合和一个德克

How can I insert into a set and a deque

本文关键字:一个 集合 德克 插入      更新时间:2023-10-16

我有一个C++函数,它需要将一系列连续的整数插入到一个集合中,并在取消排队结束时以与迭代相同的顺序插入该集合的每个新元素。 下面是一个大约为 O(log(n( * n( 的解决方案,因为重复插入每个都是 O(log(n((。 我想得到一个 O(n( 解决方案。 我想使用采用提示迭代位置的 set::insert((,但如果我这样做,我不知道如何在恒定时间内确定该项目是否已经在集合中。

#include <deque>
#include <set>
void
insertUnique(const int beginOffset,
             const int endOffset,
             std::set<int> &sent,
             std::deque<int> &recent)
{
  for (int offset = beginOffset; offset < endOffset; ++offset) {
    const bool inserted = sent.insert(offset).second;
    if (inserted) {
      recent.push_back(offset);
    }
  }
}

有没有办法将其重构为 O(n( 并完成相同的工作,同时保持函数的参数不变? 有没有办法使用迭代器提示插入并知道是否插入了该项?

如果sent仅用于确定整数是否已排队,那么我建议使用std::unordered_set,因为所有插入和搜索都有平均常量时间。

但是,除非你的套装会变得很大,否则它不太可能产生太大的影响。

事实上,如果记录的不同整数的数量小于 ~1000,那么你甚至可以使用向量获得更好的实际性能,特别是如果你保持它的排序 - 因为std::find()使用二进制搜索,这是 O(logN( 时间,但没有指针取消引用并且具有良好的内存局部性。

编辑:

只是为了好玩,我尝试了几次,但问题是sent是一个set<>,没有比 O(logN( 更快的插入方法。

这个将集合复制到unordered_set(平均恒定时间操作(,但最终插入是logN :-(

void
insertUnique_average_constant(const int beginOffset,
                              const int endOffset,
                              std::set<int> &sent,
                              std::deque<int> &recent)
{
    std::unordered_set<int> temp_sent(begin(sent), end(sent));
    for (int offset = beginOffset; offset < endOffset; ++offset) {
        const bool inserted = temp_sent.insert(offset).second;
        if (inserted) {
            recent.push_back(offset);
        }
    }
    sent.insert(begin(temp_sent), end(temp_sent));
}

如果你能忍受的话,这个可能会有一些希望。它试图使用set_difference(O2n(来减少必须发送然后缓存的项目集的大小,因此理论上随着sent集的扩展,它的效率会提高。

// a minimal iterator that simply serves the next int in sequence
struct next_index_iterator
{
    using value_type = int;
    next_index_iterator(int value)
    : _value(value)
    {}
    bool operator==(const next_index_iterator& that) const {
        return this->_value == that._value;
    }
    next_index_iterator& operator++() {
        ++_value;
        return *this;
    }
    next_index_iterator operator++(int) {
        return next_index_iterator(_value + 1);
    }
    const int& operator*() const {
        return _value;
    }
private:
    int _value;
};
// necessary for set_difference to compile    
namespace std {
    template<>
    struct iterator_traits<next_index_iterator>
    {
        using value_type = next_index_iterator::value_type;
    };
}

void
insertUnique_sort_of_2N(const int beginOffset,
                              const int endOffset,
                              std::set<int> &sent,
                              std::deque<int> &recent)
{
    std::vector<int> to_send;
    to_send.reserve(endOffset - beginOffset); 
    // set_difference is O(2N)
    std::set_difference(std::begin(sent), std::end(sent),
                        next_index_iterator(beginOffset),
                        next_index_iterator(endOffset),
                        std::back_inserter(to_send));
    // at this point to_send contains only the offsets we have not sent
    for (const auto offset : to_send) {
        recent.push_back(offset);
    }
    // but we still have to insert these into `sent` at the end    
    sent.insert(std::begin(to_send), std::end(to_send));
}