2

So.. I am trying to find the maximum value of a vector and its position in the vector. I am using a for loop, and it's working fine. My problem is, that if the maximum value appears more than once, I want to know all the positions in which it appears in the vector.. So, how could I manage this? So far, this is the code I am using: (the vector called v has elements that I read from a file but I will not add that part of the code)

std::vector<double>v;
double maxvalue;
int position=0;
maxvalue = v[0];

for (unsigned int i=0; i<v.size(); i++){
    if (v[i]> maxvalue){
        maxvalue=v[i];
        position= i;
    }
}
3
  • 3
    You then need a vector of indexes. reset with new maximum, append with duplicated maximum. Commented Apr 21, 2020 at 18:05
  • 3
    or two passes, find maximum values, and then, collect indexes with specific value. Commented Apr 21, 2020 at 18:06
  • How about std::nth_element? That should split the top n of Max from the other Commented Apr 21, 2020 at 18:39

4 Answers 4

3

You could modify your approach to keep a vector of indices where the maximum occurred:

#include <cfloat>
#include <iostream>
#include <utility>
#include <vector>

std::pair<double, std::vector<std::size_t>> FindMaxElements(std::vector<double> const& v)
{
    std::vector<std::size_t> indices;
    double current_max = -DBL_MAX;

    for (std::size_t i = 0; i < v.size(); ++i)
    {
        if (v[i] > current_max)
        {
            current_max = v[i];
            indices.clear();
        }

        if (v[i] == current_max)
        {
            indices.push_back(i);
        }
    }

    return std::make_pair(current_max, indices);
}

int main()
{
    auto result = FindMaxElements({1, 4, 7, 2, 7, 3});
    std::cout << "max: " << result.first << '\n';
    std::cout << "indices: ";
    for (auto i : result.second)
         std::cout << i << ' ';
}

Output

max: 7
indices: 2 4
Sign up to request clarification or add additional context in comments.

Comments

2

Here is a two-pass version using the standard library (whereas it might be cleaner without it):

#include <vector>
#include <algorithm>

int main()
{
    std::vector<double> v {/* fill it*/ };
    std::vector<int> pos;

    auto it = std::max_element(std::begin(v), std::end(v));
    while (it != std::end(v))
    {
        pos.push_back(std::distance(std::begin(v), it));
        it = std::find(std::next(it), std::end(v), *it);
    }
    //...
}

2 Comments

"assuming v is not empty" - why not check for that before calling std::max_element()? Or, just change val to *it when calling std::find().
@RemyLebeau: I like the second suggestion, edited, thanks.
1

The function template below, find_maximums(), returns an std::vector<size_t> that contains the positions where the maximums are in the input vector. Note that it returns an empty vector of indexes if the input vector is empty.

template<typename T>
auto find_maximums(const std::vector<T>& v) {
   std::vector<size_t> indexes;

   for (auto it_max = std::max_element(v.begin(), v.end()); it_max != v.end();
        it_max = std::find(it_max+1, v.end(), *it_max))
   {
      auto index = std::distance(v.begin(), it_max);
      indexes.push_back(index);
   }

   return indexes;
}

As an example of use:

auto main() -> int {
   std::vector<int> v = {11, 7, 3, 11, 0, 7, 1, 11, 11};
   auto max_indexes = find_maximums(v);

   if (max_indexes.empty())
      return 1;

   std::cout << "max: " << v[max_indexes.front()] << std::endl;
   std::cout << "max at positions: ";
   for (auto idx: max_indexes)
      std::cout << idx << ' ';
   std::cout << '\n';
}

It outputs:

max: 11
max at positions: 0 3 7 8

Comments

0

Passing a couple of iterators and a comparator

template <class It,
          class Comp = std::less<typename std::iterator_traits<It>::value_type>>
auto max_elements_indices(It first, It last, Comp cmp = Comp{})
{
    // This function returns a vector of indices, so to get the maximum, the caller
    // should first check if the returned vector is empty and then use one of
    // those indices to retrieve the value.
    std::vector<std::size_t> indices;
    if (first == last)
        return indices;

    // Using the first element instead of a sentinel value is easier to generalize
    indices.push_back(0);
    auto value = *first;

    for (auto i = std::next(first); i != last; ++i)
    {
        // The most common case should be an element below the maximum
        if ( cmp(*i, value) )
            continue;
        else
        {
            if ( cmp(value, *i) )
            {
                value = *i;
                indices.clear();
            }
            indices.push_back(std::distance(first, i));
        }
    }
    return indices;
}

It is testable here.

3 Comments

indices.push_back(*first); is storing the value denoted by first, not the index of the value.
Now you are are pushing the iterators themselves, not indexes. To store indexes, you would need to either 1) save the first iterator to a local variable and then use std::distance(), or 2) declare a local variable for the current index that is incremented while iterating.
@RemyLebeau Correct. Again, thanks. Time to call it the day, I guess.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.