1150. Check If a Number Is Majority Element in a Sorted Array 🔒
Problem Description
You are given an integer array nums
that is sorted in non-decreasing order (ascending order) and an integer target
. Your task is to determine whether target
is a majority element in the array.
A majority element is defined as an element that appears more than nums.length / 2
times in the array. In other words, the element must occur more than half the total number of elements in the array to be considered a majority element.
Return true
if target
is a majority element, otherwise return false
.
Since the array is sorted, all occurrences of any element will be consecutive. The solution leverages binary search to efficiently find the leftmost and rightmost positions of target
in the array. By using bisect_left
to find the first occurrence and bisect_right
to find the position just after the last occurrence, we can calculate the count of target
as right - left
. If this count is greater than len(nums) // 2
, then target
is a majority element.
Intuition
The key insight comes from recognizing that the array is sorted in non-decreasing order. In a sorted array, all occurrences of the same element must be grouped together consecutively. This means if target
appears in the array, all its occurrences form a continuous segment.
To check if target
is a majority element, we need to count how many times it appears. A naive approach would be to iterate through the entire array and count occurrences, which takes O(n)
time. However, since the array is sorted, we can do better.
Instead of counting element by element, we can find the boundaries of where target
appears in the array. If we know the leftmost position where target
starts and the rightmost position where it ends, we can calculate the count directly as the difference between these positions.
Binary search is perfect for this because:
- We can use binary search to find the first occurrence of
target
(leftmost position) - We can use binary search to find the first element greater than
target
(which gives us the position right after the last occurrence) - The difference between these two positions gives us the exact count of
target
This approach reduces our time complexity from O(n)
to O(log n)
for finding both boundaries. Once we have the count, we simply check if it's greater than len(nums) // 2
to determine if target
is a majority element.
The elegance of this solution lies in transforming a counting problem into a boundary-finding problem, leveraging the sorted nature of the array to achieve logarithmic time complexity.
Learn more about Binary Search patterns.
Solution Approach
The solution leverages Python's bisect
module which provides efficient binary search operations on sorted sequences. Here's how the implementation works:
Step 1: Find the leftmost position of target
left = bisect_left(nums, target)
bisect_left(nums, target)
returns the leftmost position where target
should be inserted to keep the array sorted. If target
exists in the array, this gives us the index of its first occurrence. If target
doesn't exist, it returns the position where it would be inserted.
Step 2: Find the position after the rightmost occurrence
right = bisect_right(nums, target)
bisect_right(nums, target)
returns the rightmost position where target
should be inserted to keep the array sorted. If target
exists, this gives us the index immediately after its last occurrence. If target
doesn't exist, it returns the same position as bisect_left
.
Step 3: Calculate the count and check majority condition
return right - left > len(nums) // 2
The count of target
in the array is right - left
. We check if this count is greater than len(nums) // 2
(integer division for the floor value).
Example walkthrough:
- For
nums = [2, 4, 5, 5, 5, 5, 5, 6, 6]
andtarget = 5
:bisect_left(nums, 5)
returns2
(first occurrence of 5)bisect_right(nums, 5)
returns7
(position after last 5)- Count =
7 - 2 = 5
- Array length =
9
, so9 // 2 = 4
- Since
5 > 4
, returntrue
Edge cases handled:
- If
target
doesn't exist in the array, bothleft
andright
will be the same value, makingright - left = 0
, which correctly returnsfalse
- The solution works for arrays of any size, including single-element arrays
Time Complexity: O(log n)
for two binary search operations
Space Complexity: O(1)
as we only use constant extra space
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 a small example to illustrate the solution approach.
Example: nums = [1, 2, 3, 3, 3, 3, 4]
, target = 3
Step 1: Find the leftmost position of target (3) Using binary search to find where 3 first appears:
- Initial array:
[1, 2, 3, 3, 3, 3, 4]
- Indices:
[0, 1, 2, 3, 4, 5, 6]
bisect_left(nums, 3)
performs binary search:- Middle element at index 3 is 3, but we need the leftmost 3
- Search continues in left half
- Finds first 3 at index 2
- Result:
left = 2
Step 2: Find the position after the rightmost occurrence of target (3) Using binary search to find where we'd insert a value just greater than 3:
bisect_right(nums, 3)
performs binary search:- Looks for the position after the last 3
- Finds that index 6 (where 4 is) is the first position greater than 3
- Result:
right = 6
Step 3: Calculate count and check majority
- Count of target:
right - left = 6 - 2 = 4
- Array length:
7
- Majority threshold:
7 // 2 = 3
- Is
4 > 3
? Yes! - Return:
true
The target 3 appears 4 times in an array of length 7, which is more than half the array size, making it a majority element.
Visual representation:
Array: [1, 2, 3, 3, 3, 3, 4] Index: 0 1 2 3 4 5 6 ↑ ↑ left right (first 3) (after last 3) Count = 6 - 2 = 4 occurrences of 3
Solution Implementation
1from typing import List
2from bisect import bisect_left, bisect_right
3
4class Solution:
5 def isMajorityElement(self, nums: List[int], target: int) -> bool:
6 """
7 Check if target is a majority element in the sorted array.
8 A majority element appears more than n/2 times in the array.
9
10 Args:
11 nums: A sorted list of integers
12 target: The integer to check if it's a majority element
13
14 Returns:
15 True if target appears more than n/2 times, False otherwise
16 """
17 # Find the leftmost position where target could be inserted to maintain sorted order
18 # This gives us the index of the first occurrence of target (if it exists)
19 left_index = bisect_left(nums, target)
20
21 # Find the rightmost position where target could be inserted to maintain sorted order
22 # This gives us the index after the last occurrence of target (if it exists)
23 right_index = bisect_right(nums, target)
24
25 # Calculate the count of target elements by subtracting indices
26 target_count = right_index - left_index
27
28 # Check if target appears more than half the length of the array
29 return target_count > len(nums) // 2
30
1class Solution {
2 /**
3 * Checks if the target value appears more than n/2 times in the sorted array.
4 * Uses binary search to find the range of target occurrences.
5 *
6 * @param nums sorted array of integers
7 * @param target the value to check for majority
8 * @return true if target appears more than n/2 times, false otherwise
9 */
10 public boolean isMajorityElement(int[] nums, int target) {
11 // Find the leftmost position of target
12 int leftBoundary = findFirstPosition(nums, target);
13
14 // Find the leftmost position of (target + 1), which gives us the right boundary
15 int rightBoundary = findFirstPosition(nums, target + 1);
16
17 // Check if the count of target elements is more than half the array length
18 return (rightBoundary - leftBoundary) > nums.length / 2;
19 }
20
21 /**
22 * Binary search to find the leftmost position where value >= x.
23 * This finds the first occurrence of x if it exists, or the position
24 * where x would be inserted to maintain sorted order.
25 *
26 * @param nums sorted array to search in
27 * @param x target value to find
28 * @return index of the first position where nums[i] >= x
29 */
30 private int findFirstPosition(int[] nums, int x) {
31 int left = 0;
32 int right = nums.length;
33
34 // Binary search for the leftmost position
35 while (left < right) {
36 // Calculate middle index using bit shift for efficiency
37 int mid = (left + right) >> 1;
38
39 if (nums[mid] >= x) {
40 // If mid element is >= x, search in left half (including mid)
41 right = mid;
42 } else {
43 // If mid element is < x, search in right half (excluding mid)
44 left = mid + 1;
45 }
46 }
47
48 return left;
49 }
50}
51
1class Solution {
2public:
3 bool isMajorityElement(vector<int>& nums, int target) {
4 // Find the first position where target appears in the sorted array
5 auto leftBound = lower_bound(nums.begin(), nums.end(), target);
6
7 // Find the position just after the last occurrence of target
8 auto rightBound = upper_bound(nums.begin(), nums.end(), target);
9
10 // Calculate the count of target elements by subtracting iterators
11 // Check if this count is greater than half of the array size
12 return (rightBound - leftBound) > (nums.size() / 2);
13 }
14};
15
1/**
2 * Checks if a target value appears more than n/2 times in a sorted array
3 * @param nums - A sorted array of numbers
4 * @param target - The target value to check for majority
5 * @returns true if target appears more than n/2 times, false otherwise
6 */
7function isMajorityElement(nums: number[], target: number): boolean {
8 /**
9 * Binary search to find the leftmost position where value >= x
10 * @param x - The value to search for
11 * @returns The index of the leftmost position where nums[i] >= x
12 */
13 const findLeftmostPosition = (x: number): number => {
14 let left: number = 0;
15 let right: number = nums.length;
16
17 // Binary search for the leftmost position
18 while (left < right) {
19 // Calculate middle index using bit shift for efficiency
20 const mid: number = (left + right) >> 1;
21
22 if (nums[mid] >= x) {
23 // If mid element is >= x, search in left half
24 right = mid;
25 } else {
26 // If mid element is < x, search in right half
27 left = mid + 1;
28 }
29 }
30
31 return left;
32 };
33
34 // Find the first occurrence of target
35 const firstOccurrence: number = findLeftmostPosition(target);
36
37 // Find the first occurrence of the next value after target
38 // This gives us the position right after the last occurrence of target
39 const positionAfterLastOccurrence: number = findLeftmostPosition(target + 1);
40
41 // Calculate the count of target elements
42 const targetCount: number = positionAfterLastOccurrence - firstOccurrence;
43
44 // Check if target count is more than half of the array length
45 // Using bit shift (>> 1) to divide by 2 for efficiency
46 return targetCount > (nums.length >> 1);
47}
48
Time and Space Complexity
The time complexity of this solution is O(log n)
, where n
is the length of the array nums
. This is because the algorithm uses two binary search operations (bisect_left
and bisect_right
) to find the leftmost and rightmost positions of the target element in the sorted array. Each binary search operation takes O(log n)
time, and since we perform two binary searches sequentially, the overall time complexity remains O(log n)
(as O(log n) + O(log n) = O(log n)
).
The space complexity is O(1)
as the algorithm only uses a constant amount of extra space to store the variables left
and right
, regardless of the input size. The binary search operations are performed in-place without requiring additional space proportional to the input size.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Majority Element Definition
A common mistake is checking if the count equals n/2
instead of checking if it's strictly greater than n/2
. The majority element must appear more than half the times, not exactly half or at least half.
Incorrect Implementation:
# Wrong: checking >= instead of >
return right - left >= len(nums) // 2
Correct Implementation:
# Correct: strictly greater than n/2
return right - left > len(nums) // 2
2. Not Handling Empty Arrays
While the bisect functions handle empty arrays gracefully (both return 0), it's good practice to explicitly handle this edge case for clarity and to avoid unnecessary operations.
Enhanced Solution:
def isMajorityElement(self, nums: List[int], target: int) -> bool:
if not nums: # Handle empty array
return False
left = bisect_left(nums, target)
right = bisect_right(nums, target)
return right - left > len(nums) // 2
3. Confusion Between bisect_left and bisect_right
Developers sometimes confuse which function to use or attempt to use bisect_left
for both boundaries, leading to off-by-one errors.
Incorrect Approach:
# Wrong: using bisect_left for both boundaries left = bisect_left(nums, target) right = bisect_left(nums, target + 1) # This works but is less intuitive
Better Approach:
# Clear and intuitive left = bisect_left(nums, target) # First occurrence right = bisect_right(nums, target) # After last occurrence
4. Manual Binary Search Implementation Errors
Some developers try to implement binary search manually instead of using the bisect module, leading to boundary condition errors.
Error-Prone Manual Implementation:
def findLeft(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1 # Easy to mess up boundaries
return left
Recommended Solution:
Use Python's built-in bisect
module which is well-tested and optimized. If manual implementation is required, carefully test boundary conditions.
5. Integer Division vs Float Division
Using float division (/
) instead of integer division (//
) can cause issues when comparing with the count.
Incorrect:
# Wrong: float division might cause comparison issues
return right - left > len(nums) / 2
Correct:
# Correct: integer division for proper comparison
return right - left > len(nums) // 2
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!