Kittu20 has discovered a set of problems typically described using terms like
"number of occurrences",
"distribution" or
"frequency", or
"histogram".
There are many applicable algorithms that differ in their behaviour and requirements. For example, if we already have all the data in an array, we can use an offline algorithm; but if we get the data element by element and need the results on an ongoing basis, we use an
online algorithm. (Don't let the terms 'online' and 'offline' confuse you: these are not related to the 'online' and 'offline' used for example network connectivity.)
Online algorithms for this generally use a data structure that stores not only the values to be counted, but the number of occurrences of that specific value.
Offline algorithms vary much more, especially depending on how many unique values you're interested in compared to the number of values you have.
Hash tables can be used when the order of the values is irrelevant; optimum hash table size is about twice the length of the input. Sorted data structures can be used if the data needs to be sorted anyway. And so on.
The methods described in previous messages in this thread show the array or "histogram" approach. Each array element corresponds to one value (or a range of values for a proper histogram, technically), describing the number of occurrences of that value. In the examples, the values shown are between 0 and 9, inclusive, so an array of ten possible values are used. Often, we do an initial loop of all known values to find the minimum and maximum, and allocate an array large enough to hold everything in between. (Note that in C on current 64-bit architectures, you can dynamically allocate an array large enough to describe a 32-bit count for each possible 32-bit integer (or floating-point) value. Such an array is 16 GiB in size, though. If you try to declare such an array as a static (global) or local variable, it will usually fail; you do need to use
malloc(),
calloc(), or
realloc() to dynamically allocate the memory for such an array.)
As always, there are tradeoffs.
In general, for very short arrays, say up to a dozen or two entries, the two nested loop approach will likely be just as fast as the array option, because the number of loop iterations is so small: for
N=16,
N²=256, and 256 iterations of a simple loop body will be ridiculously fast anyway.
The exact cutoff, be it 5 or 16 or whatever, varies depending on the machine and even on the array item type.
For arrays where the range of values is shorter than say twice the number of elements in the array, the array or histogram approach tends to be the fastest.
For arrays where the range of values is larger than say twice the number of elements in the array, a hash table preallocated to about twice the number of elements in the array (to keep the access time short, by ensuring the fill level is at most 50%), tends to be the fastest.
When one wants to use as little memory as possible, the two nested loop approach is perfectly acceptable; it will just become slower proportionally more with larger arrays.
I could show examples of a generic histogram/occurrences data structure and the three approaches for using them, and a single function call interface that selects which one is best used for the specified data (via the above
heuristic, with the exact limits set by compile-time constants), but I suspect it would be too much too soon for Kittu20, and probably not that interesting to others. So, instead, I'll just leave this as a wall-of-text post. Apologies for that.