min n元素与昂贵或删除的默认构造函数

时间:2012-11-19 05:09:12

标签: c++ stl default-constructor

给定一个数组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的最小元素作为输出。就这样。我认为这是一个现实的限制。

2 个答案:

答案 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);
}