LeetCode 169 - Majority Element
The problem asks us to identify the majority element in a given array of integers nums. The majority element is defined as the number that appears more than half of the times in the array, i.e., more than ⌊n / 2⌋ times where n is the length of the array.
Difficulty: 🟢 Easy
Topics: Array, Hash Table, Divide and Conquer, Sorting, Counting
Solution
Problem Understanding
The problem asks us to identify the majority element in a given array of integers nums. The majority element is defined as the number that appears more than half of the times in the array, i.e., more than ⌊n / 2⌋ times where n is the length of the array. The input array can contain both positive and negative integers, and the length can range from 1 to 50,000. The problem guarantees that a majority element always exists, which simplifies the solution because we do not have to handle the case where no majority exists.
In other words, we are asked to scan the array and find the element that dominates the array in frequency. The expected output is this element itself. Constraints indicate that the array could be large, so inefficient solutions might not be acceptable. Key edge cases include arrays with a single element, arrays where all elements are the same, and arrays where the majority element is at the beginning, middle, or end.
Approaches
The simplest brute-force method is to count the frequency of each element. We can do this using a hash map (dictionary in Python or map in Go) where the key is the element and the value is its count. After counting, we iterate through the map to find the element whose count exceeds n // 2. While this approach is correct, it requires O(n) space and O(n) time.
A better solution leverages the Boyer-Moore Voting Algorithm, which relies on the observation that the majority element appears more than half the time. By maintaining a candidate and a counter, we can traverse the array once, incrementing the counter when the current element matches the candidate and decrementing it otherwise. If the counter drops to zero, we pick the current element as the new candidate. By the end of the array, the candidate is guaranteed to be the majority element. This approach achieves O(n) time and O(1) space, which meets the follow-up requirement.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n) | O(n) | Count frequencies using a hash map and find the element exceeding n/2 |
| Optimal (Boyer-Moore) | O(n) | O(1) | Maintain a candidate and counter to identify majority element in one pass |
Algorithm Walkthrough
- Initialize
candidateasNoneandcountas0. This sets up a placeholder for tracking the potential majority element. - Iterate over each element
numin the arraynums. - If
countis0, updatecandidateto the currentnum. This means we are starting a new potential majority. - If
numequalscandidate, incrementcount. This reinforces our current candidate. - If
numdoes not equalcandidate, decrementcount. This cancels out a previous occurrence of a different element. - After traversing the entire array, return
candidate. The algorithm guarantees thatcandidateis the majority element because the majority element cannot be completely canceled out by other elements.
Why it works: Since the majority element appears more than half the time, every time it encounters a different element, the counter only reduces by one. Because the majority element has more occurrences than all other elements combined, it will remain as the candidate at the end.
Python Solution
from typing import List
class Solution:
def majorityElement(self, nums: List[int]) -> int:
candidate = None
count = 0
for num in nums:
if count == 0:
candidate = num
count += 1 if num == candidate else -1
return candidate
The implementation initializes the candidate and counter. It iterates through nums, updating the candidate whenever the count drops to zero, and adjusts the counter according to whether the current element matches the candidate. The final candidate is returned as the majority element.
Go Solution
func majorityElement(nums []int) int {
candidate := 0
count := 0
for _, num := range nums {
if count == 0 {
candidate = num
}
if num == candidate {
count++
} else {
count--
}
}
return candidate
}
The Go implementation mirrors the Python version. We use integer variables candidate and count. The main difference is that Go does not have built-in lists with dynamic resizing like Python, but slices provide the same functionality for iteration. No nil handling is needed because the array is guaranteed to have at least one element.
Worked Examples
Example 1: nums = [3,2,3]
| Step | num | candidate | count | Explanation |
|---|---|---|---|---|
| 1 | 3 | None -> 3 | 0 -> 1 | count=0, pick 3 as candidate |
| 2 | 2 | 3 | 1 -> 0 | 2 != 3, decrement count |
| 3 | 3 | 2 -> 3 | 0 -> 1 | count=0, pick 3 as new candidate |
Return 3.
Example 2: nums = [2,2,1,1,1,2,2]
| Step | num | candidate | count | Explanation |
|---|---|---|---|---|
| 1 | 2 | None -> 2 | 0 -> 1 | pick 2 |
| 2 | 2 | 2 | 1 -> 2 | match, increment |
| 3 | 1 | 2 | 2 -> 1 | mismatch, decrement |
| 4 | 1 | 2 | 1 -> 0 | mismatch, decrement, count=0 |
| 5 | 1 | 1 | 0 -> 1 | pick new candidate 1 |
| 6 | 2 | 1 | 1 -> 0 | mismatch, decrement, count=0 |
| 7 | 2 | 2 | 0 -> 1 | pick new candidate 2 |
Return 2.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n) | Single pass through the array, constant time per element |
| Space | O(1) | Only two integer variables are used, no extra data structures |
The linear time complexity ensures scalability up to the maximum allowed input size, and constant space meets the follow-up requirement.
Test Cases
# provided examples
assert Solution().majorityElement([3,2,3]) == 3 # simple odd-length array
assert Solution().majorityElement([2,2,1,1,1,2,2]) == 2 # majority at end
# single element
assert Solution().majorityElement([5]) == 5 # edge case, array of length 1
# all elements same
assert Solution().majorityElement([1,1,1,1,1]) == 1 # all identical
# majority at start
assert Solution().majorityElement([9,9,8,8,9]) == 9 # majority at beginning
# majority in middle
assert Solution().majorityElement([4,4,5,4,5,4,6]) == 4 # majority in middle
| Test | Why |
|---|---|
| [3,2,3] | Odd-length, majority in first position |
| [2,2,1,1,1,2,2] | Majority element at the end, mix of elements |
| [5] | Single-element edge case |
| [1,1,1,1,1] | All elements identical |
| [9,9,8,8,9] | Majority at the start |
| [4,4,5,4,5,4,6] | Majority in the middle with other elements interspersed |
Edge Cases
One important edge case is an array with a single element. The algorithm handles this correctly because the candidate is initialized as None and updated immediately to the single element, which is trivially the majority. Another edge case is an array where all elements are identical. Here, the counter will increment repeatedly without any decrements, resulting in the correct majority. Finally, arrays where the majority element appears at irregular positions, such as at the start, middle, or end, could trip up naive algorithms that stop after finding a local maximum; the Boyer-Moore algorithm correctly aggregates counts across the entire array. In all these cases, the algorithm ensures correctness by maintaining a running candidate and counter invariant.