DSAPrep
EasyArrays & Hashing

Majority Element

Given an array nums of size n, return the majority element.

The majority element is the element that appears more than floor(n / 2) times in the array. You may assume that the majority element always exists in the array.

Example 1

            Input: nums = [3,2,3]
            Output: 3
            

            
                Explanation: 3 appears twice and 2 appears once. Since floor(3 / 2) = 1, only 3 passes the threshold.
              
          

Example 2

            Input: nums = [2,2,1,1,1,2,2]
            Output: 2
            

            
                Explanation: 2 appears four times โ€” more than floor(7 / 2) = 3 โ€” while 1 appears three times. 2 is the majority.
              
          

Constraints

  • n == nums.length
  • 1 <= n <= 5 * 10^4
  • -10^9 <= nums[i] <= 10^9
View original on LeetCode โ†—

The pattern to take away is a surplus argument: the majority element owns more than half of the array, so its votes outnumber every rival vote combined. That means even after pairing each rival against one majority vote โ€” each 1 cancels one 2 โ€” a majority vote is left standing. Boyer-Moore turns that fact into a one-pass walk: hold one candidate and one lead counter, let each rival read consume one candidate vote, and when the lead hits 0 let the next read claim the slot. A value that holds a positive lead after every rival pair has been cancelled must be the majority.

Brute Force: Hash Map Counting

Time O(n)Space O(n)

The definition of a majority is a threshold: a value appears more than floor(n / 2) times. The most direct reading of that definition is to count everything and then pick the value that clears the bar. One pass fills a hash map with the tally of every value; a second pass over the map finds the count greater than n // 2. Because the statement guarantees a majority exists, the map is guaranteed to contain the winner โ€” the unreachable return is only for the type checker.

class Solution:
def majorityElement(self, nums: list[int]) -> int:
n = len(nums)
counts = {}
for num in nums:
counts[num] = counts.get(num, 0) + 1
for num, count in counts.items():
if count > n // 2:
return num
return -1 # unreachable: the statement guarantees a majority

Why it still costs O(n) space: the running time is already optimal at O(n) โ€” any correct solution must at least read every element once โ€” so the only thing this approach wastes is memory. In the worst case the array holds n distinct values and the map grows to n entries, so the space bill is O(n). A micro-optimization is easy: check the threshold inside the first loop and return the instant some count crosses it, since floor(n / 2) is fixed once n is known. But the map stays the same size, and that extra memory is exactly what the next solution eliminates.

Boyer-Moore Majority Vote

OptimalTime O(n)Space O(1)

Boyer-Moore keeps exactly two variables: a candidate and a lead (the count). Walk the array once. If the lead is 0, the current value claims the candidate slot with a lead of 1. Otherwise a read that matches the candidate raises the lead by 1, and any different read lowers it by 1 โ€” each rival vote cancelling one candidate vote. When the lead returns to 0, an equal-size block of rivals has cancelled the candidate block exactly, so the whole cancelled prefix is forgotten and the next read restarts the race.

class Solution:
def majorityElement(self, nums: list[int]) -> int:
candidate = None
count = 0
for num in nums:
if count == 0: # no lead: current value claims the slot
candidate = num
count = 1
elif num == candidate: # same team: extend the lead
count += 1
else: # rival vote: cancels one candidate vote
count -= 1
return candidate

Why the majority is guaranteed to win: write M for the true majority. It appears more than floor(n / 2) times, so its votes outnumber all other votes combined across the entire array. Every time the lead hits 0, the prefix scanned since the last claim held exactly as many candidate votes as rival votes โ€” no value owned more than half of that prefix โ€” and cancelling such a tied prefix cannot reduce Mโ€™s advantage in the rest of the array. The votes still standing at the end are therefore Mโ€™s unpaired surplus, and the final candidate is exactly that surplus. The lead-0 moments in the traces below are these equal-block cancellations happening in real time.

Why it is optimal: the single pass reads each of n elements once for O(n) time โ€” matching the hash map โ€” but keeps only two integers, so space is O(1). Time cannot improve below O(n) because every element must be examined, and space cannot improve below O(1), so this solution is optimal on both axes.

Watch the lead on the file example, nums = [2, 2, 1, 1, 1, 2, 2] โ€” the block of three 1s first cancels the 2 lead to 0, then a 1 claims the slot, then a 2 wipes that lead and rebinds. The two depose moments are the entire mechanism:

start

nums

2โ–ผ
2โ–ผ
1โ–ผ
1โ–ผ
1โ–ผ
2โ–ผ
2โ–ผ

candidate

โ€”

lead (count)

โ€”
nothing held yet

cancelled

0rival votes erased so far

invariant: each rival vote cancels one candidate vote โ€” lead 0 means equal blocks collided exactly, and the next read claims the slot

1 / 9
currentseencomparingdiscardedresult

A majority appears more than floor(n / 2) times, so its votes outnumber all rivals combined. Boyer-Moore uses one candidate and one lead: each rival read consumes one candidate vote; lead 0 means equal-size blocks cancelled.

Then a shorter second look, nums = [3, 2, 3] โ€” a majority that loses its whole lead mid-array and still wins on its surplus:

start

nums

3โ–ผ
2โ–ผ
3โ–ผ

candidate

โ€”

lead (count)

โ€”
nothing held yet

cancelled

0rival votes erased so far

invariant: each rival vote cancels one candidate vote โ€” lead 0 means equal blocks collided exactly, and the next read claims the slot

1 / 5
currentseencomparingdiscardedresult

A short companion to the file example: with three elements the majority needs just 2 votes, and 3 has them. This trace shows a majority dipping all the way to a 0 lead mid-array and still recovering on its surplus.