具有昂贵或已删除的默认构造函数的最小n个元素
min n elements with expensive or deleted default constructor
给定一个通常未排序数据(如assert(std::is_same< typeof(v), V >::value);
)的数组v
(一些STL容器,例如std::vector< double >
)。在数组的元素上定义了比较运算符,比如std::less
。您需要创建一个具有n
最小元素的数组(从v
复制),但这些元素是,不是默认可构造的(或者是昂贵的操作)。如何使用STL?需要非修改序列算法。
最初被视为使用std::back_insert_iterator
解决问题的一种方法,但如进一步解释的那样,存在一些混淆:
assert(!std::is_default_constructible< typename V::value_type >::value); // assume
template< class V >
V min_n_elements(typename V::const_iterator begin, typename V::const_iterator end, typename V::size_type const n)
{
assert(!(std::distance(begin, end) < n));
V result; // V result(n); not allowed
result.reserve(n);
std::partial_sort_copy(begin, end, std::back_inserter(result), /*What should be here? mb something X(result.capacity())?*/, std::less< typename V::value_type >());
return result;
}
我想找到在时间和内存方面最优的解决方案(O(1)额外内存和<=O(std::partial_sort_copy
)时间消耗)。总的来说,算法应该在以下数量的存储器上操作:不可修改源v
的v.size()
元素作为输入,新建元素的n
作为输出,所有这些元素都是源阵列v
的n
最小元素的副本。仅此而已。我认为这是一个现实的极限。
EDIT:用堆重新实现:
template< class V >
V min_n_elements(typename V::const_iterator b, typename V::const_iterator e, typename V::size_type const n) {
assert(std::distance(b, e) >= n);
V res(b, b+n);
make_heap(res.begin(), res.end());
for (auto i=b+n; i<e; ++i) {
if (*i < res.front()) {
pop_heap(res.begin(), res.end());
res.back() = *i;
push_heap(res.begin(), res.end());
}
}
return std::move(res);
}
除非您还需要对这些元素进行排序,否则使用std::nth_element
,然后使用std::copy
可能是最简单、最快的。
template <class InIter, class OutIter>
min_n_elements(InIter b, InIter e, OutIter o, InIter::difference_type n) {
InIter pos = b+n;
std::nth_element(b, pos, e);
std:copy(b, pos, o);
}
std::nth_element
不仅找到给定的元素,而且保证那些小于该元素的元素是它的"左边"的两个,而那些大于该元素的则是在它的"右边"。
不过,这确实稍微回避了真正的问题——它并没有真正为结果创建容器,而是简单地期望用户创建一个正确类型的容器,然后提供一个迭代器(例如,back_insert_iterator)来将数据放在正确的位置。同时,我认为这确实是正确的做法——找到N个最小元素的算法和为目的地选择容器是分开的。
如果你真的想把结果放在一个特定的容器类型中,那应该不是很难:
template <class V>
V n_min_element(V::iterator b, V::iterator e) {
V::const_iterator pos = b+n;
nth_element(b, pos, e);
V ret(b, pos);
return V;
}
就目前情况来看,它们确实修改了输入中的(元素的顺序),但鉴于您已经说过输入没有排序,我假设它们的顺序无关紧要,所以这应该是允许的。如果你不能做到这一点,下一种可能性是可能创建一个指针集合,并使用一个基于指针进行比较的比较函数,然后对此执行nth_element,最后将指针复制到新集合。
- 复制包含C++所有元素的对象!(构造函数和赋值,最佳实践?
- 如何为 std::vector 分配内存,然后稍后为某些元素调用构造函数?
- 如何在 c++ 中将整个数组初始化为类构造函数中的单个元素
- 无法在构造函数中执行设置元素插入
- 如何在构造函数初始值设定项列表中使用 n 个元素初始化 std::vector<std::time_t>
- 如何在类中制作 2D 数组元素,然后在其构造函数中指定其维度?
- std::tuple默认构造函数,带有move可构造元素
- 如果我想从类型"T"定义元素的容器(来自 STL),那么"T"必须使用默认构造函数?
- 为 unordered_map 中的元素设置默认构造函数(如果是 [] 运算符)
- 在 new 关键字中,由默认构造函数初始化的类中的元素是否也使用 new 关键字在C++?
- 为什么只有在向量中已经有一个元素时才调用移动构造函数?
- 我们如何使用下面的集合构造函数构造设置元素
- clang格式:如何将构造函数的初始值设定项列表的每个元素保存在单独的行上
- 在不调用构造函数的情况下创建 Vector 元素
- C2280 STD :: vector的默认构造函数尽管具有移动构造函数,但仍需要元素的复制构造函数
- 实例化 std::vector 的唯一元素,而无需复制构造函数
- 覆盖数组元素时如何调用构造函数
- 在分配器感知类调用复制构造函数中对向量元素的引用
- 将元素添加到矢量时复制构造函数
- 无法使用迭代器擦除矢量元素,矢量元素实现移动构造函数