Search code examples
c++stldefault-constructor

min n elements with expensive or deleted default constructor


Given an array v (some STL container, e.g. std::vector< double >) of generally unsorted data (say assert(std::is_same< typeof(v), V >::value);). Over the elements of the array is defined comparison operator, say std::less. You need to create an array with n minimal elements (copies form v), but the elements are not default constructible (or is expensive operation). How to do it by means of STL? Non-modifying sequence algorithm is required.

Originally seen as a way to solve using std::back_insert_iterator, but there is some confusion as explained further:

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;
}

I want to find solution that is optimal in terms of time and memory (O(1) additional memory and <= O(std::partial_sort_copy) time consumption). Totally algorithm should operate on the following number of memory: v.size() elements of non-modifiable source v as input and n of newly created elements, all of which are copies of the n smallest elements of source array v, as output. That's all. I think this is a realistic limits.


Solution

  • EDIT: reimplemented with heap:

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