There are several possible solutions. Most answers suggest creating a (partially) sorted copy of the array, or of an array of indices. However, that potentially requires a lot of extra storage. If the size of the input is very large, the extra storage might not fit in the cache anymore, and then this might become slow.
As an alternative, you can scan the input array only once, and keep a set of 5 indices of the largest elements seen so far. Below is a possible implementation, that is a bit more generic and works on any container that provides ForwardIterators:
template<typename Iterator>
std::vector<size_t> n_largest_indices(Iterator it, Iterator end, size_t n) {
struct Element {
Iterator it;
size_t index;
};
std::vector<Element> top_elements;
top_elements.reserve(n + 1);
for(size_t index = 0; it != end; ++index, ++it) {
top_elements.insert(std::upper_bound(top_elements.begin(), top_elements.end(), *it, [](auto value, auto element){return value > *element.it;}), {it, index});
if (index >= n)
top_elements.pop_back();
}
std::vector<size_t> result;
result.reserve(top_elements.size());
for(auto &element: top_elements)
result.push_back(element.index);
return result;
}
The above could probably be improved further, and specialized for RandomAccessIterators so you don't need to have a struct element anymore, and just have to store the top n indices. It could also easily be modified to return the n largest values themselves or to return iterators to the n largest values.
Example usage:
std::vector<int> v{1, 35, 12, 69, 2, 5,1, 6, 99, 53, 2};
auto indices = n_largest_indices(v.begin(), v.end(), 5);
// Print indices
for(auto i: indices)
std::cout << i << " ";
std::cout << "\n";
// Print values corresponding to the indices
for(auto i: indices)
std::cout << v[i] << " ";
std::cout << "\n";
Result:
8 3 9 1 2
99 69 53 35 12