Opening the reading…
Opening the reading…
HASHING › IMPLEMENTARY PROBLEMS
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →A value must appear more than n/3 times, so three different values cannot all cross that threshold: their combined occurrences would exceed n. Therefore the answer has at most two elements. The algorithm keeps two candidate slots, because no valid answer needs a third slot.
When the current number matches a candidate, that candidate gains support. When a candidate slot is empty, the number takes it. If both slots already hold different values and the current number matches neither, remove one unit of support from both candidates. This cancels a group of three different values. Removing such a group cannot eliminate a value that occurs more than n/3 times, because that value cannot be part of enough cancellations to disappear completely.
The first pass leaves possible candidates, not guaranteed answers. A candidate can survive because of the cancellation process even when its actual frequency is too small. The second pass counts the two survivors in the original array and applies the exact condition count > floor(n/3).
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n) | The first pass examines each element once and the verification pass examines each element once more. Each operation inside either pass is constant time, so the two complete scans add to 2n operations, which is O(n). |
| Space | O(1) extra | The algorithm stores only two candidate values, two counts, and the result's constant-sized maximum of two values. The returned result is required output and is excluded from working space; the bound remains O(1) even for the worst input shape. |
#include <vector>
using namespace std;
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int n = nums.size();
int candidate1 = 0;
int candidate2 = 0;
int count1 = 0;
int count2 = 0;
for (int num : nums) {
if (num == candidate1) {
count1++;
} else if (num == candidate2) {
count2++;
} else if (count1 == 0) {
candidate1 = num;
count1 = 1;
} else if (count2 == 0) {
candidate2 = num;
count2 = 1;
} else {
count1--;
count2--;
}
}
count1 = 0;
count2 = 0;
for (int num : nums) {
if (num == candidate1) {
count1++;
} else if (num == candidate2) {
count2++;
}
}
vector<int> result;
if (count1 > n / 3) {
result.push_back(candidate1);
}
if (count2 > n / 3) {
result.push_back(candidate2);
}
return result;
}
};The order of the first four conditions matters. A matching candidate must be counted before an empty slot is reused, and the two candidates must be checked before a replacement is attempted. The final pass deliberately resets both counts: the first-pass counts measure uncancelled support, not the true frequencies required by the statement.
A frequency map is the direct approach: count every value, then collect the entries whose counts exceed n/3. It is easier to explain and less sensitive to the order of candidate checks, but it stores up to n distinct values. The voting solution is an optimisation from O(n) extra space to O(1); hashing is still a reasonable choice when memory is not the concern.
#include <unordered_map>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> majorityElement(vector<int>& nums) {
int n = nums.size();
unordered_map<int, int> frequency;
vector<int> result;
for (int num : nums) {
frequency[num]++;
}
for (const auto& entry : frequency) {
if (entry.second > n / 3) {
result.push_back(entry.first);
}
}
return result;
}
};The map version takes expected O(n) time and O(n) extra space, because it visits the array once and may keep one entry for every distinct value. It buys simpler reasoning and removes the need for verification candidates, while the two-candidate method buys the constant-space bound without changing the linear time.