Difficulty: Easy, Asked-in: Facebook, Uber.
Key takeaway: An excellent problem to learn problem-solving using sorting and hash table.
Given an array X[] of size n, write a program to find the most frequent element in the array, i.e., the element that occurs the maximum number of times.
Input: X[] = [2, 12, 1, 2, 8, 2, 2, 1, 8, 2], Output: 2
Explanation: 2 is the most frequent element, which appears 4 times.
Input: X[] = [1, 9, 1, 1, 2], Output: 1
Explanation: 1 is a single repeated element that appears 3 times.
Input: X[] = [3, 8, 2, 3, 2], Output: 2
Explanation: 2 and 3 are repeated two times each. So we return the smallest of them, which is 2.
The basic idea is to count the occurrences of each element and return the element with the maximum number of occurrences. We can implement this using a nested loop where we pick each element using the outer loop and scan the entire array to count its frequency using the inner loop.
Step 1: We initialize two variables outside the nested loops: maxFreq to track the maximum frequency and mostFrequent to track the most frequent element.
Step 2: We run the outer loop from i = 0 to n - 1 to pick each element and the inner loop from j = 0 to n - 1 to count the frequency of that element. Before starting the inner loop, we also initialize a variable countFreq to track the frequency count of the current element.
Step 3: By the end of the nested loop, we return the value stored in mostFrequent.
int mostFrequentElement(int X[], int n)
{
int maxFreq = 0
int mostFrequent = -1
for (int i = 0; i < n; i = i + 1)
{
int countFreq = 1
for (int j = 0; j < n; j = j + 1)
{
if (X[j] == X[i])
countFreq = countFreq + 1
}
if (maxFreq < countFreq)
{
maxFreq = countFreq
mostFrequent = X[i]
}
else if (maxFreq == countFreq)
mostFrequent = min(mostFrequent, X[i])
}
return mostFrequent
}
We are running a nested loop and performing an O(1) operation at each iteration. Time complexity = n*n*O(1) = O(n^2). We are using a constant number of extra variables, so space complexity = O(1).
Now, the critical question is: How can we improve the time complexity? Searching is an essential operation in the problem. Can we think to improve the time complexity of searching to improve the overall complexity?
If we sort the array, then all duplicate elements will get placed adjacent to each other. We can easily scan the array, count the frequency of each element, and return the element with the max frequency. Compared to the above approach, this ensures that the frequency is calculated only once for each unique element.
Step 1: We first sort the array using some efficient O(nlogn) sorting algorithm like merge sort, heap sort, or quicksort. Let's suppose we are using heap sort, which works in place.
Step 2: We initialize two variables: maxFreq to track the maximum frequency and mostFrequent to track the most frequent element.
Step 3: Now we scan the sorted array using a loop until i < n. Inside the loop, we initialize the variable countFreq to track the frequency count of the current element.
Step 4: By the end of the outer loop, we return the value stored in mostFrequent.
int mostFrequentElement(int X[], int n)
{
sort(X, X + n)
int maxFreq = 0
int mostFrequent = -1
int i = 0
while (i < n)
{
int countFreq = 1
while (i + 1 < n && X[i] == X[i + 1])
{
countFreq = countFreq + 1
i = i + 1
}
if (maxFreq < countFreq)
{
maxFreq = countFreq
mostFrequent = X[i]
}
else if (maxFreq == countFreq)
mostFrequent = min(mostFrequent, X[i])
i = i + 1
}
return mostFrequent
}
If we observe the nested while loops, we are incrementing the value of i in each iteration of either the outer loop or the inner loop. So total number of nested loop iterations = n. In other words, we are accessing each element only once and performing O(1) operation. So time complexity of nested loop = n * O(1) = O(n).
The overall time complexity = Time complexity of heap sort + Time complexity of the nested loop = O(nlogn) + O(n) = O(nlogn). Space complexity = O(1), because heap sort works in place, and we are using a constant number of variables.
Again, the critical question arises: How can we improve the time complexity? Can we use a hash table to efficiently perform searching? A hash table allows for basic dictionary operations such as insertion, deletion, and searching in O(1) average time. So, here is the idea:
int mostFrequentElement(int X[], int n)
{
HashMap<int, int> H
int maxFreq = 1
int mostFrequent = -1
for (int i = 0; i < n; i = i + 1)
{
if (H.search(X[i]) == true)
{
H[X[i]] = H[X[i]] + 1
if (maxFreq < H[X[i]])
{
maxFreq = H[X[i]]
mostFrequent = X[i]
}
else if (maxFreq == H[X[i]])
mostFrequent = min(mostFrequent, X[i])
}
else
H[X[i]] = 1
}
return mostFrequent
}
We perform a single scan of the array and, at each iteration, carry out constant operations. In other words, we search, insert, or update each element once in the hash table. So time complexity = n * O(1) = O(n). The space complexity = O(n) as we store the elements in the hash table.
If you have any queries or feedback, please write us at contact@enjoyalgorithms.com. Enjoy learning, Enjoy algorithms!