2

I had this on an interview question. I'd like to see how StackOverflow would do it.

What would Bjarne Stroustrop think of my way? It's a little wordy, but I don't know how to make it better, unfortunately. I know you guys are gonna laugh at my stupidity.

template <class T>
T mode(T* arr, size_t n)
// If there's a tie, return an arbitrary element tied for 1st
// If the array size is 0, throw an error
{
   if (n == 0)
   {
       throw("Mode of array of size 0 is undefined, bro.");
   }
   else if (n == 1)
   {
       return arr[0];
   }
   else 
   {
      std::pair<T, int> highest(arr[0], 1);
      std::map<T, int> S;
      S.insert(highest);
      for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)
      {
          if (S.count(*thisPtr) == 0)
          {
             S.insert(std::pair<T, int> (*thisPtr, 1);
          }
          else 
          {
             ++S[*thisPtr];
             if (S[*thisPtr] > highest.second)
             {
                 highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
             }
          }
      }
   }
}
  • 1
    Maybe look at this? http://stackoverflow.com/questions/3798261/find-most-occurence-element-in-array?rq=1 – merlin2011 Mar 24 '14 at 03:34
  • You could replace the contents of your `for` loop with `if ( ++S[*thisPtr] > highest.second ) highest = ...` – M.M Mar 24 '14 at 06:02

3 Answers3

1

You could do this, provided that T implements std::hash:

std::unordered_multiset<T> elems;
std::for_each(arr, arr + size, [&elems](T const & elem) { elems.insert(elem); }

//Now you have elems.count() for each entry
auto max_count = /*Guaranteed minimum value*/
T mode{};
for (auto const & set_elem : elems) {
    if (max(elems.count(set_elem), max_count) == max_count)) {
      mode = set_elem;
    }
}
Germán Diago
  • 7,473
  • 1
  • 36
  • 59
1

I think I'd use an std::map to do the counting, then find the item with the largest count:

template <class T>
T mode(T* arr, size_t n) {
    std::map<T, size_t> counts;

    for (size_t i=0; i<n; i++)
        ++counts[arr[i]];

    return max_element(counts.begin(), counts.end(), 
        [](std::pair<T, size_t> const &a, std::pair<T, size_t> const &b) {
            return a.second < b.second;
        })->first;
}

If you expect a really large number of unique items, you might want to use an std::unordered_map instead of a std::map [should reduce expected complexity from O(n log n) to O(N)].

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
1

I found the following issues with your code.

Redundant check n == 1

You can remove the block

else if (n == 1)
{
    return arr[0];
}

without affecting the outcome.

Declaration of the variables in the for loop:

T* thisPtr(arr + 1), lastPtr(arr+n);`

is equivalent to

T* thisPtr(arr + 10); T lastPtr(arr+n);

That's not what your intention is. The compiler will report an error too. So, move their declaration outside the for loop. Change

for (T* thisPtr(arr + 1), lastPtr(arr+n); thisPtr != lastPtr; ++thisPtr)

to

T* thisPtr(arr + 1);
T* lastPtr(arr+n);
for ( ; thisPtr != lastPtr; ++thisPtr)

Simplify the contents of the for loop

The lines

if (S.count(*thisPtr) == 0)
{
   S.insert(std::pair<T, int> (*thisPtr, 1));
}

can be replaced by

 ++S[*thisPtr];

which is exactly what you are doing in the following else block.

You can change the contents of the entire for loop to:

++S[*thisPtr];
if (S[*thisPtr] > highest.second)
{
   highest = std::pair<T, int> (*thisPtr, S[*thisPtr]);
}

You need to return the mode

Add

  return highest.first;

before the closing of the else block.

R Sahu
  • 204,454
  • 14
  • 159
  • 270