给定一个数组v
(一些 STL 容器,例如std::vector< double >
)通常未排序的数据(比如assert(std::is_same< typeof(v), V >::value);
)。在数组的元素上定义了比较运算符,比如说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)额外内存和&lt; = O(std::partial_sort_copy
)时间消耗方面最佳的解决方案。完全算法应该在以下数量的内存上运行:作为输入的不可修改源v.size()
的{{1}}元素和新创建的元素的v
,所有这些元素都是{{1}的副本源数组n
的最小元素作为输出。就这样。我认为这是一个现实的限制。
答案 0 :(得分:2)
除非您还需要对这些元素进行排序,否则使用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
不仅可以找到给定的元素,还可以保证那些小于2的元素是“左”,而那些更大的元素则是“正确”。
这确实可以解决实际问题 - 而不是实际为结果创建容器,它只是希望用户创建一个正确类型的容器,然后提供一个迭代器(例如,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,最后复制指向新系列的人。
答案 1 :(得分:2)
编辑:用堆重新实现:
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);
}