Skip to content

Commit 49e2318

Browse files
committed
officially solve Majority Element
1 parent a86a9de commit 49e2318

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

problems/majority_element.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from typing import List
2+
3+
4+
class OfficialSolution:
5+
"""
6+
Approach 6: Boyer-Moore Voting Algorithm
7+
8+
== Intuition ==
9+
If we had some way of counting instances of the majority element as +1 and instances
10+
of any other element as -1, summing them would make it obvious that the majority
11+
element is indeed the majority element.
12+
13+
== Algorithm ==
14+
Essentially, what Boyer-Moore does is look for a suffix suf of nums where suf[0] is
15+
the majority element in that suffix. To do this, we maintain a count, which is
16+
incremented whenever we see an instance of our current candidate for majority
17+
element and decremented whenever we see anything else. Whenever count equals 0, we
18+
effectively forget about everything in nums up to the current index and consider the
19+
current number as the candidate for majority element. It is not immediately obvious
20+
why we can get away with forgetting prefixes of nums - consider the following
21+
examples (pipes are inserted to separate runs of nonzero count):
22+
[7,7,5,7,5,1|5,7|5,5,7,7|7,7,7,7]
23+
Here, the 7 at index 0 is selected to be the first candidate for majority element.
24+
count will eventually reach 0 after index 5 is processed, so the 5 at index 6 will
25+
be the next candidate. In this case, 7 is the true majority element, so by
26+
disregarding this prefix, we are ignoring an equal number of majority and minority
27+
elements - therefore, 7 will still be the majority element in the suffix formed by
28+
throwing away the first prefix.
29+
[7,7,5,7,5,1|5,7|5,5,7,7|5,5,5,5]
30+
Now, the majority element is 5 (we changed the last run of the array from 7s to 5s),
31+
but our first candidate is still 7. In this case, our candidate is not the true
32+
majority element, but we still cannot discard more majority elements than minority
33+
elements (this would imply that count could reach -1 before we reassign candidate,
34+
which is obviously false).
35+
Therefore, given that it is impossible (in both cases) to discard more majority
36+
elements than minority elements, we are safe in discarding the prefix and attempting
37+
to recursively solve the majority element problem for the suffix. Eventually, a
38+
suffix will be found for which count does not hit 0, and the majority element of
39+
that suffix will necessarily be the same as the majority element of the overall
40+
array.
41+
42+
== Complexity Analysis ==
43+
Time Complexity: O(n).
44+
Boyer-Moore performs constant work exactly n times, so the algorithm runs in
45+
linear time.
46+
Space Complexity: O(1).
47+
Boyer-Moore allocates only constant additional memory.
48+
"""
49+
50+
def majorityElement(self, nums: List[int]) -> int:
51+
count = 0
52+
candidate = None
53+
for num in nums:
54+
if count == 0:
55+
candidate = num
56+
count += 1 if num == candidate else -1
57+
return candidate

tests/test_majority_element.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import unittest
2+
3+
from majority_element import OfficialSolution
4+
5+
6+
class TestMajorityElement(unittest.TestCase):
7+
def test_example_1(self):
8+
assert OfficialSolution().majorityElement(nums=[3, 2, 3]) == 3
9+
10+
def test_example_2(self):
11+
assert OfficialSolution().majorityElement(nums=[2, 2, 1, 1, 1, 2, 2]) == 2

0 commit comments

Comments
 (0)