Problem. Find the element appearing more than times in an array, if one exists.
Boyer-Moore voting — time, space
int majority(const vector<int>& a) {
int cand = a[0], count = 0;
for (int x : a) {
if (count == 0) cand = x;
count += (x == cand) ? 1 : -1;
}
// VERIFY — a majority may not exist
int c = 0;
for (int x : a) if (x == cand) c++;
return c * 2 > (int)a.size() ? cand : -1;
}The intuition: pair each occurrence of the candidate with a non-occurrence and cancel them. A true majority element survives every cancellation, because it has more copies than everything else combined.
Always verify
The algorithm returns some element even when no majority exists. The second pass is not optional. This is the most common mistake with this algorithm.
See Boyer-Moore Voting.
The generalisation: elements appearing more than times
At most such elements exist. Keep candidates with counts; when a new element matches none and all counters are non-zero, decrement all of them.
vector<int> majorityK(const vector<int>& a, int k) {
map<int,int> cnt; // at most k-1 entries
for (int x : a) {
if (cnt.count(x)) cnt[x]++;
else if ((int)cnt.size() < k - 1) cnt[x] = 1;
else {
for (auto it = cnt.begin(); it != cnt.end(); )
if (--it->second == 0) it = cnt.erase(it); else ++it;
}
}
// verify each candidate with a second pass
vector<int> res;
for (auto& [v, _] : cnt) {
int c = count(a.begin(), a.end(), v);
if (c * k > (int)a.size()) res.push_back(v);
}
return res;
}time, space. This is the Misra-Gries algorithm, and it is the standard streaming “heavy hitters” primitive.
The alternatives
| Method | Time | Space | Note |
|---|---|---|---|
| Boyer-Moore voting | optimal; needs verification | ||
| Hash map counting | simplest; also gives all counts | ||
| Sort, take the middle | the majority must occupy index | ||
| Randomised sampling | expected | pick a random element, verify; succeeds with probability | |
| Bit-by-bit majority | for each bit, take the majority bit | ||
| Divide and conquer | the majority of the whole is a majority of some half |
The sort-and-take-the-middle observation is worth remembering: if an element occupies more than half the array, it must cover position after sorting. One line, if a sort is affordable.
Range majority queries
“What is the majority element of ?” — harder, and there are several routes:
| Method | Cost |
|---|---|
| Randomised: sample positions in the range and verify each | per query, high probability |
| Segment tree with Boyer-Moore merge | per query + verification |
| Persistent segment tree over values | , exact |
| Mo’s algorithm |
The segment tree merge works because the Boyer-Moore pair (candidate, count) is mergeable: combining two ranges’ candidates cancels the smaller count. Verification still requires counting occurrences, typically with a sorted position list per value plus binary search.
Streaming relatives
| Problem | Algorithm | Space |
|---|---|---|
| Majority | Boyer-Moore | |
| Elements | Misra-Gries | |
| Approximate frequencies | Count-Min sketch | |
| Distinct count | HyperLogLog | |
| Uniform sample | reservoir sampling |
Why it is worth knowing
Boyer-Moore voting is the cleanest example of a cancellation argument: the answer survives because it outnumbers everything else combined. That framing — pair up and cancel — appears in parity arguments, in XOR tricks for “find the unique element”, and in several streaming algorithms.
See also: Boyer-Moore Voting · Reservoir Sampling · Segment Tree