我有一个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)并完成相同的工作,同时保持函数的参数不变?有没有办法插入迭代器提示,并知道该项是否已插入?
答案 0 :(得分:1)
如果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));
}