Leetcode 169: Majority element

Leetcode 169: Majority element

data structures and algorithms

·

7 min read

If you would rather learn visually, watch the video here --> youtu.be/enVivn6eXJY

Understand the problem

Leetcode 169 - majority element: Given an array nums of size n, return the majority element.

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

Follow-up: Could you solve the problem in linear time and in O(1) space?

  1. We are given an array of integers
  2. The majority element is the element that occurs more times than half the length of the array

This means that if the length of an array is 7; half the length of the array is ( 7 // 2 ) = 3 The majority element has to occur more than 3 times

Brute force approach

The brute force approach can be solved using the storage technique

Algorithm:

  1. Loop over the items in the array
  2. Then keep track of each item and the number of occurrences --- either in an object or hashmap
  3. Return the item with the most occurrence

using map

const majorityElement = function(nums) {
const map = new Map()
let [maxCount, res] = [0, 0]

  for(let i = 0; i < nums.length; i++){
      map.set(nums[i], map.get(nums[i]) + 1 || 1)

      if(map.get(nums[i]) > maxCount){
          res = nums[i]
          maxCount = map.get(nums[i])
      } 
  }  

  return res
};

using object

const majorityElement = function(nums) {
    const obj = {}
    let [res, maxCount] = [0, 0]

    for(let i = 0; i < nums.length; i++){
        obj[nums[i]] = obj[nums[i]] + 1 || 1

        if(obj[nums[i]] > maxCount){
            res = nums[i]
            maxCount = obj[nums[i]]
        }
    }
    return res
};

Complexity Analysis

Time complexity: The time complexity of the storage technique is linear 0(n); this is because the code will run for the entire input size given, and the solution will run as large as the input size.

Space complexity: The space complexity is linear 0(n), the storage technique uses an extra data structure which can also grow as large as the input size

Follow up

follow-up.png

We have a follow-up: can we solve this problem in constant space?

There are a number of techniques that can be used to accomplish this feat - none of them come intuitively - and some of them include divide and conquer, bit manipulation, boyer Moore's algorithm etc.

In this article, we will look at boyer Moore's algorithm.

Boyer Moore's algorithm

According to Wikipedia;

The Boyer–Moore majority vote algorithm is an algorithm for finding the majority of a sequence of elements using linear time and constant space. It is named after Robert S. Boyer and J Strother Moore, who published it in 1981,[1] and is a prototypical example of a streaming algorithm.

The algorithm is also written as this:

  • Initialise a variable for element m and a counter variable
  • For each element x
    • If the counter is 0
      • then the element m = element x
        • otherwise:
        • if element x == element m:
          • increment the counter
        • decrement the counter
  • return element m

Make it make sense, please...

The algorithm follows the survival of the fittest logic.

Let's examine the code sample below of an array with integers where at least one of them is a majority element:

[2,2,1,1,1,2,2]

Boyer Moore's method asks that we initialize two variables; m and counter - the m represents the majority element and the counter represents the number of times it has occurred.

At the start of the operation, they are initialized to 0

let [m, counter] = [0, 0]

Next, we loop over the given elements in the array. If the counter is equal to 0, the current value during iteration becomes the majority element and we store it in the variable m. This is the assumption the algorithm follows.

let [m, counter] = [0, 0]

for (let num of nums){
    if (counter === 0){
        m = num
    }
}

Outside of the conditional, we make the following check:

  • is the value in the m variable the same as the value in num;
    • if it is, we increment the counter by 1
    • else, we decrement the counter by 1

This statement can be translated into this code;

if (num  === m) counter += 1
else counter += -1

Or, we can use the ternary operator to simplify it:

counter += num === m ?  1 : - 1

Now, let's bring our code together

let [m, counter] = [0, 0]

for (let num of nums){
    if (counter === 0){
        m = num
    }
counter += num === m ?  1 : - 1
}

Continuing with our code analysis. On the first iteration; the element in our loop is 2

image.png

so we run through our algorithm quickly:

  • is counter === 0; yes it is
    • since the counter is 0, the current element becomes the majority element
    • m = 2
  • is the value of m the same as the current element in the iteration
    • yes; m = 2, while current num = 2
    • so, go ahead and increment the counter variable

And our most occurring element and counter are 2 and 1 respectively

m = 2, counter = 1

We move over to the next element in our iteration - 2

image.png

Algorithm:

  • is counter === 0; no it's not, it's 1
  • is the value of m the same as the current element in the iteration
    • yes; they are both 2
    • so, go ahead and increment the counter variable

m = 2, counter = 2

Next element in the iteration - 1

image.png

Algorithm:

  • is counter === 0; no it's not, it's 2
  • is the value of m the same as the current element in the iteration
    • no; m = 2, while current num = 1
    • so, go ahead and decrement the counter variable

m = 2, counter = 1

Next element in the iteration - 1

image.png

Algorithm:

  • is counter === 0; no it's not, it's 1
  • is the value of m the same as the current element in the iteration
    • no; m = 2, while current num = 1
    • so, go ahead and decrement the counter variable

m = 2, counter = 0

Next element in the iteration - 1

image.png

Algorithm:

  • is counter === 0; yes it is
    • since the counter is 0, the current element becomes the majority element
    • m = 1
  • is the value of m the same as the current element in the iteration
    • yes; m = 1, while current num = 1
    • so, go ahead and increment the counter variable

m = 1, counter = 1

Next element in the iteration - 2

image.png

Algorithm:

  • is counter === 0; no it is 1
  • is the value of m the same as the current element in the iteration
    • no; m = 1, while current num = 2
    • so, go ahead and decrement the counter variable

m = 1, counter = 0

Last element in iteration - 2

image.png

Algorithm:

  • is counter === 0; yes it is
    • since the counter is 0, the current element becomes the majority element
    • m = 2
  • is the value of m the same as the current element in the iteration
    • yes; m = 2, while current num = 2
    • so, go ahead and increment the counter variable

m = 2, counter = 1

This is the last element in the array; return m

Find the full code here

You can try this algorithm for a wide range of test cases, but as long as there is one element that is a majority, it would be the last man standing and cancel out the other occurrences.

If you would rather watch the video --> youtu.be/enVivn6eXJY

🍵 Happy coding