169. Majority Element
Problem Description
You are given an array nums
of size n
. Your task is to find and return the majority element in this array.
The majority element is defined as the element that appears more than βn / 2β
times in the array. In other words, it's an element that occurs more than half the time in the array.
For example:
- In an array of size 7, the majority element must appear at least 4 times
- In an array of size 8, the majority element must appear at least 5 times
The problem guarantees that a majority element always exists in the given array, so you don't need to handle cases where no majority element is present.
The solution implements the Boyer-Moore Voting Algorithm, which is an efficient way to find the majority element in a single pass through the array. The algorithm works by maintaining a candidate element m
and a counter cnt
:
- Start with
cnt = 0
- For each element
x
in the array:- If
cnt
is 0, set the current element as the new candidate (m = x
) and setcnt = 1
- Otherwise, if the current element equals the candidate (
m == x
), increment the counter - If the current element is different from the candidate, decrement the counter
- If
The key insight is that the majority element will "survive" this voting process because it appears more than half the time. Every time a non-majority element decrements the counter, there are still enough majority elements left to maintain dominance.
Intuition
Think of this problem as a voting competition where different candidates (array elements) are trying to win. The key insight is that if one candidate appears more than half the time, they have an absolute majority - they can't be defeated even if all other candidates team up against them.
Imagine pairing up elements in the array. When we see two different elements, we can "cancel them out" - like opposing votes nullifying each other. Since the majority element appears more than n/2
times, even after all possible cancellations with different elements, the majority element will still have leftover occurrences.
This leads us to a clever counting strategy: we maintain a candidate and a counter. The counter represents how many "uncanceled" votes our current candidate has. When we encounter the same element as our candidate, it strengthens their position (increment counter). When we encounter a different element, it challenges the candidate (decrement counter).
When the counter reaches zero, it means all previous votes have been canceled out, so we start fresh with a new candidate. The beautiful part is that the true majority element will always survive this process because:
- If the majority element is currently the candidate, there aren't enough other elements to completely cancel it out
- If a non-majority element becomes the candidate temporarily, it will eventually be overthrown because it doesn't have enough support
By the end of the array, the surviving candidate must be the majority element. This works because we're guaranteed a majority exists - if we remove pairs of different elements from the array, the majority element will always remain due to its dominance in numbers.
This approach transforms what could be a counting problem requiring extra space (like using a hash map) into an elegant single-pass solution with O(1)
space complexity.
Solution Approach
The implementation uses the Boyer-Moore Voting Algorithm, which processes the array in a single pass with constant space complexity.
Let's walk through the algorithm step by step:
Initialization:
- Set
cnt = 0
(counter for tracking the current candidate's strength) - Set
m = 0
(variable to store the current candidate element)
Main Loop:
For each element x
in the array nums
:
-
Check if counter is zero (
if cnt == 0
):- This means we either just started or all previous votes have been canceled
- Set the current element as the new candidate:
m = x
- Initialize the counter to 1:
cnt = 1
-
Otherwise, update the counter based on the current element:
- If
m == x
(current element matches our candidate):- Increment the counter:
cnt = cnt + 1
- This strengthens the candidate's position
- Increment the counter:
- If
m != x
(current element is different from our candidate):- Decrement the counter:
cnt = cnt - 1
- This represents a cancellation between different elements
- Decrement the counter:
- If
Return the result:
After processing all elements, return m
as the majority element.
Why this works:
- The majority element appears more than
n/2
times - Even if every non-majority element tries to cancel out the majority element (by decrementing its counter), there aren't enough of them to completely eliminate it
- The final candidate
m
that survives must be the majority element
Time and Space Complexity:
- Time Complexity:
O(n)
- single pass through the array - Space Complexity:
O(1)
- only using two variables (cnt
andm
)
The elegance of this algorithm lies in its ability to find the majority element without explicitly counting occurrences or using additional data structures like hash maps.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the Boyer-Moore Voting Algorithm with a concrete example.
Example array: nums = [2, 2, 1, 1, 1, 2, 2]
In this array of size 7, the majority element must appear more than 3 times. Let's trace through the algorithm:
Initial state:
cnt = 0
m = 0
(placeholder, will be set when cnt is 0)
Step-by-step execution:
Step | Current Element (x) | cnt before | Action | m (candidate) | cnt after |
---|---|---|---|---|---|
1 | 2 | 0 | cnt is 0, set new candidate | 2 | 1 |
2 | 2 | 1 | x equals m, increment | 2 | 2 |
3 | 1 | 2 | x differs from m, decrement | 2 | 1 |
4 | 1 | 1 | x differs from m, decrement | 2 | 0 |
5 | 1 | 0 | cnt is 0, set new candidate | 1 | 1 |
6 | 2 | 1 | x differs from m, decrement | 1 | 0 |
7 | 2 | 0 | cnt is 0, set new candidate | 2 | 1 |
Final result: m = 2
Let's verify: Element 2 appears 4 times in the array (positions 0, 1, 5, 6), which is more than β7/2β = 3, confirming it's the majority element.
Key observations from this walkthrough:
- When we encounter matching elements (steps 1-2), the counter increases, strengthening the candidate
- When different elements appear (steps 3-4), they "cancel out" with the candidate
- When the counter reaches 0 (steps 5, 7), we reset with a new candidate
- The true majority element (2) eventually emerges as the final candidate despite temporarily losing the position to element 1
This demonstrates how the majority element survives the voting process due to its numerical dominance in the array.
Solution Implementation
1class Solution:
2 def majorityElement(self, nums: List[int]) -> int:
3 """
4 Find the majority element using Boyer-Moore Voting Algorithm.
5 The majority element appears more than n/2 times in the array.
6
7 Args:
8 nums: List of integers
9
10 Returns:
11 The majority element
12 """
13 count = 0
14 candidate = 0
15
16 # Boyer-Moore Voting Algorithm
17 for num in nums:
18 if count == 0:
19 # When count reaches 0, select current element as new candidate
20 candidate = num
21 count = 1
22 else:
23 # Increment count if current element matches candidate
24 # Decrement count if current element differs from candidate
25 if candidate == num:
26 count += 1
27 else:
28 count -= 1
29
30 # The candidate at the end is guaranteed to be the majority element
31 # (given that a majority element exists as per problem constraints)
32 return candidate
33
1class Solution {
2 public int majorityElement(int[] nums) {
3 // Boyer-Moore Voting Algorithm to find majority element
4 // The majority element appears more than n/2 times
5
6 int count = 0; // Counter for current candidate
7 int candidate = 0; // Current majority element candidate
8
9 // Iterate through all elements in the array
10 for (int num : nums) {
11 // If count reaches 0, select current element as new candidate
12 if (count == 0) {
13 candidate = num;
14 count = 1;
15 } else {
16 // If current element matches candidate, increment count
17 // Otherwise, decrement count (cancellation)
18 if (candidate == num) {
19 count++;
20 } else {
21 count--;
22 }
23 }
24 }
25
26 // The remaining candidate is the majority element
27 // (Problem guarantees a majority element exists)
28 return candidate;
29 }
30}
31
1class Solution {
2public:
3 int majorityElement(vector<int>& nums) {
4 // Boyer-Moore Voting Algorithm to find the majority element
5 // The majority element appears more than n/2 times
6
7 int count = 0; // Counter for the current candidate
8 int candidate = 0; // Current candidate for majority element
9
10 // Iterate through all elements in the array
11 for (int& num : nums) {
12 if (count == 0) {
13 // When count reaches 0, select current element as new candidate
14 candidate = num;
15 count = 1;
16 } else {
17 // Increment count if current element matches candidate,
18 // otherwise decrement count
19 count += (candidate == num) ? 1 : -1;
20 }
21 }
22
23 // The candidate at the end is guaranteed to be the majority element
24 // (given that a majority element exists as per problem constraints)
25 return candidate;
26 }
27};
28
1/**
2 * Finds the majority element in an array using Boyer-Moore Voting Algorithm.
3 * The majority element is the element that appears more than βn/2β times.
4 *
5 * @param nums - The input array of numbers
6 * @returns The majority element in the array
7 */
8function majorityElement(nums: number[]): number {
9 // Counter to track the current candidate's advantage
10 let count: number = 0;
11
12 // Current candidate for majority element
13 let candidate: number = 0;
14
15 // Iterate through each number in the array
16 for (const currentNumber of nums) {
17 // If count reaches 0, select a new candidate
18 if (count === 0) {
19 candidate = currentNumber;
20 count = 1;
21 } else {
22 // Increment count if current number matches candidate,
23 // otherwise decrement count
24 count += (candidate === currentNumber) ? 1 : -1;
25 }
26 }
27
28 // Return the majority element candidate
29 return candidate;
30}
31
Time and Space Complexity
The time complexity is O(n)
, where n
is the length of the array nums
. The algorithm iterates through the array exactly once, performing constant-time operations (comparisons and arithmetic operations) for each element.
The space complexity is O(1)
. The algorithm only uses two variables (cnt
and m
) to track the candidate element and its count, regardless of the input size. No additional data structures are created that scale with the input.
Common Pitfalls
1. Attempting to Verify the Result When Not Required
A common mistake is adding unnecessary verification code after the Boyer-Moore algorithm to confirm that the candidate is indeed the majority element:
# Unnecessary verification
candidate = boyer_moore_result
count = sum(1 for num in nums if num == candidate)
if count > len(nums) // 2:
return candidate
else:
return -1 # This will never happen given the problem constraints
Why it's a pitfall: The problem explicitly guarantees that a majority element exists, so verification adds unnecessary O(n) time and makes the code more complex without any benefit.
2. Misunderstanding When Counter Resets
Some implementations incorrectly reset the counter only when it becomes negative:
# Incorrect approach if count < 0: # Wrong condition! candidate = num count = 1
The correct approach: The counter should reset when it reaches exactly 0, not when it becomes negative:
if count == 0: candidate = num count = 1
3. Incorrect Counter Update Logic
A subtle but critical mistake is updating the counter before checking if we need a new candidate:
# Incorrect order of operations for num in nums: if candidate == num: count += 1 else: count -= 1 if count == 0: # This check happens too late! candidate = num count = 1
Why it fails: When count becomes 0 after decrementing, we should immediately set the NEXT element as the candidate, not the current one that just caused the decrement.
Correct approach:
for num in nums: if count == 0: candidate = num count = 1 elif candidate == num: count += 1 else: count -= 1
4. Using the Algorithm When Majority Element May Not Exist
If you use Boyer-Moore in a scenario where a majority element might not exist (different from this problem), the algorithm will still return a candidate, but it won't necessarily be a majority element:
# Example where Boyer-Moore returns wrong result without verification nums = [1, 2, 3] # No majority element exists # Boyer-Moore would return 3, but it's not a majority element
Solution: In problems where majority element existence isn't guaranteed, you must add a verification pass:
def majorityElement(nums):
# First pass: Find candidate
count = 0
candidate = 0
for num in nums:
if count == 0:
candidate = num
count = 1
elif candidate == num:
count += 1
else:
count -= 1
# Second pass: Verify (only needed if majority element might not exist)
if nums.count(candidate) > len(nums) // 2:
return candidate
return None # No majority element exists
5. Initializing Candidate with Invalid Value
While initializing candidate = 0
works fine (since it gets overwritten immediately), some might try to use special values that could cause issues:
# Potentially problematic if nums could contain None candidate = None
Better practice: Since the candidate is always set in the first iteration when count == 0
, the initial value doesn't matter, but using a simple integer like 0 avoids any potential type-related issues.
Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?
Recommended Readings
https assets algo monster cover_photos dnd svg Divide and Conquer The main idea of divide and conquer is to split the main problem into two smaller components assuming that each one of the components had already been solved recursively and then try to solve the bigger problem using the solutions
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donβt Miss This!