如何插入集合和双端队列

时间:2014-12-14 16:33:27

标签: c++ time-complexity deque stdset

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

#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)并完成相同的工作,同时保持函数的参数不变?有没有办法插入迭代器提示,并知道该项是否已插入?

1 个答案:

答案 0 :(得分:1)

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

然而,除非你的场景变得庞大,否则不太可能产生太大的影响。

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

编辑:

只是为了好玩,我做了几次尝试,但问题是sentset<>,没有比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));
}