Facebook Pixel

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:

  1. Start with cnt = 0
  2. For each element x in the array:
    • If cnt is 0, set the current element as the new candidate (m = x) and set cnt = 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

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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. If the majority element is currently the candidate, there aren't enough other elements to completely cancel it out
  2. 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:

  1. 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
  2. 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
    • If m != x (current element is different from our candidate):
      • Decrement the counter: cnt = cnt - 1
      • This represents a cancellation between different elements

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 and m)

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 Evaluator

Example 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:

StepCurrent Element (x)cnt beforeActionm (candidate)cnt after
120cnt is 0, set new candidate21
221x equals m, increment22
312x differs from m, decrement21
411x differs from m, decrement20
510cnt is 0, set new candidate11
621x differs from m, decrement10
720cnt is 0, set new candidate21

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:

  1. When we encounter matching elements (steps 1-2), the counter increases, strengthening the candidate
  2. When different elements appear (steps 3-4), they "cancel out" with the candidate
  3. When the counter reaches 0 (steps 5, 7), we reset with a new candidate
  4. 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the tree traversal order can be used to obtain elements in a binary search tree in sorted order?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More