A clear explanation of finding the element that appears more than half the time using Boyer-Moore voting.
Problem Restatement
We are given an integer array nums of size n.
We need to return the majority element.
The majority element is the value that appears more than:
n // 2times.
The problem guarantees that a majority element always exists. LeetCode also asks whether we can solve it in linear time and constant extra space.
Input and Output
| Item | Meaning |
|---|---|
| Input | Integer array nums |
| Output | The majority element |
| Majority rule | Appears more than floor(n / 2) times |
| Guarantee | A majority element always exists |
| Follow-up | Solve in O(n) time and O(1) space |
Example function shape:
def majorityElement(nums: list[int]) -> int:
...Examples
Example 1:
nums = [3, 2, 3]The value 3 appears twice.
The array length is 3, so the majority threshold is:
3 // 2 = 1Since 2 > 1, the answer is:
3Example 2:
nums = [2, 2, 1, 1, 1, 2, 2]The value 2 appears 4 times.
The array length is 7, so the majority threshold is:
7 // 2 = 3Since 4 > 3, the answer is:
2First Thought: Count Frequencies
A simple solution is to count how many times each number appears.
class Solution:
def majorityElement(self, nums: list[int]) -> int:
count = {}
for num in nums:
count[num] = count.get(num, 0) + 1
if count[num] > len(nums) // 2:
return numThis is correct and runs in O(n) time.
But it uses O(n) extra space in the worst case.
The follow-up asks for constant extra space.
Key Insight
Use Boyer-Moore voting.
Think of different numbers canceling each other out.
If one value appears more than half the time, then even after pairing it with every different value, at least one copy of it remains.
So we keep:
| Variable | Meaning |
|---|---|
candidate | Current possible majority element |
count | Candidate’s vote balance |
When we see the same value as candidate, increase count.
When we see a different value, decrease count.
If count becomes 0, all previous votes have canceled out, so we choose the next value as a new candidate.
Because the true majority appears more than all other values combined, it survives this cancellation process.
Algorithm
Initialize:
candidate = None
count = 0For each number num in nums:
- If
count == 0, setcandidate = num. - If
num == candidate, incrementcount. - Otherwise, decrement
count.
At the end, return candidate.
The problem guarantees that a majority element exists, so no second verification pass is needed.
Walkthrough
Use:
nums = [2, 2, 1, 1, 1, 2, 2]| Number | Action | Candidate | Count |
|---|---|---|---|
2 | choose candidate, same value | 2 | 1 |
2 | same as candidate | 2 | 2 |
1 | different | 2 | 1 |
1 | different | 2 | 0 |
1 | choose candidate, same value | 1 | 1 |
2 | different | 1 | 0 |
2 | choose candidate, same value | 2 | 1 |
Return:
2Even though the candidate temporarily changes to 1, the true majority 2 still survives by the end.
Correctness
The algorithm cancels pairs of different values.
Removing one majority element together with one non-majority element does not change the fact that the majority value is still the only value that can appear more than half of the remaining uncanceled elements.
Because the majority element appears more than all other elements combined, it cannot be fully canceled by different values.
Whenever count becomes zero, the processed prefix has been fully canceled into pairs of different values. That prefix cannot change the majority of the remaining suffix.
Therefore, after scanning the whole array, the remaining candidate must be the majority element.
Since the problem guarantees that a majority element exists, returning candidate is correct.
Complexity
| Metric | Value | Why |
|---|---|---|
| Time | O(n) | We scan the array once |
| Space | O(1) | Only candidate and count are stored |
Implementation
class Solution:
def majorityElement(self, nums: list[int]) -> int:
candidate = None
count = 0
for num in nums:
if count == 0:
candidate = num
if num == candidate:
count += 1
else:
count -= 1
return candidateCode Explanation
Start with no candidate:
candidate = None
count = 0When the vote balance is zero, choose the current value:
if count == 0:
candidate = numThen update the vote balance:
if num == candidate:
count += 1
else:
count -= 1A same value supports the candidate.
A different value cancels one vote.
At the end:
return candidatereturns the majority element.
Alternative: Sorting
If we sort the array, the majority element must appear at the middle index.
class Solution:
def majorityElement(self, nums: list[int]) -> int:
nums.sort()
return nums[len(nums) // 2]This works because a value that appears more than half the time must cover the median position.
Complexity:
| Metric | Value |
|---|---|
| Time | O(n log n) |
| Space | Depends on sorting implementation |
The Boyer-Moore version is better for the follow-up.
Testing
def run_tests():
sol = Solution()
assert sol.majorityElement([3, 2, 3]) == 3
assert sol.majorityElement([2, 2, 1, 1, 1, 2, 2]) == 2
assert sol.majorityElement([1]) == 1
assert sol.majorityElement([1, 1, 2]) == 1
assert sol.majorityElement([-1, -1, -1, 2, 3]) == -1
assert sol.majorityElement([6, 5, 5]) == 5
print("all tests passed")
run_tests()| Test | Why |
|---|---|
[3, 2, 3] | Basic example |
[2, 2, 1, 1, 1, 2, 2] | Candidate changes during scan |
[1] | Single element |
[1, 1, 2] | Majority appears at the start |
[-1, -1, -1, 2, 3] | Negative majority value |
[6, 5, 5] | Majority appears after first value |