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 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 301class 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 * Uses the standard binary search template with firstTrueIndex tracking. 24 * This finds the first occurrence of x if it exists, or the position 25 * where x would be inserted to maintain sorted order. 26 * 27 * @param nums sorted array to search in 28 * @param x target value to find 29 * @return index of the first position where nums[i] >= x 30 */ 31 private int findFirstPosition(int[] nums, int x) { 32 int left = 0; 33 int right = nums.length - 1; 34 int firstTrueIndex = nums.length; // Default to length if not found 35 36 // Binary search for the leftmost position 37 while (left <= right) { 38 int mid = left + (right - left) / 2; 39 40 if (nums[mid] >= x) { 41 // Found an element >= x (feasible) 42 firstTrueIndex = mid; 43 right = mid - 1; 44 } else { 45 // Mid element is < x 46 left = mid + 1; 47 } 48 } 49 50 return firstTrueIndex; 51 } 52} 531class 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}; 151/** 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 * Uses the standard binary search template with firstTrueIndex tracking 11 * @param x - The value to search for 12 * @returns The index of the leftmost position where nums[i] >= x 13 */ 14 const findLeftmostPosition = (x: number): number => { 15 let left = 0; 16 let right = nums.length - 1; 17 let firstTrueIndex = nums.length; // Default to length if not found 18 19 // Binary search for the leftmost position 20 while (left <= right) { 21 const mid = Math.floor((left + right) / 2); 22 23 if (nums[mid] >= x) { 24 // Found an element >= x (feasible) 25 firstTrueIndex = mid; 26 right = mid - 1; 27 } else { 28 // Mid element is < x 29 left = mid + 1; 30 } 31 } 32 33 return firstTrueIndex; 34 }; 35 36 // Find the first occurrence of target 37 const firstOccurrence: number = findLeftmostPosition(target); 38 39 // Find the first occurrence of the next value after target 40 // This gives us the position right after the last occurrence of target 41 const positionAfterLastOccurrence: number = findLeftmostPosition(target + 1); 42 43 // Calculate the count of target elements 44 const targetCount: number = positionAfterLastOccurrence - firstOccurrence; 45 46 // Check if target count is more than half of the array length 47 // Using bit shift (>> 1) to divide by 2 for efficiency 48 return targetCount > (nums.length >> 1); 49} 50Solution 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
targetdoesn't exist in the array, bothleftandrightwill 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
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. Using Wrong Binary Search Template Variant
A critical mistake is using the wrong binary search variant. The correct template uses while left <= right with right = mid - 1 and tracks the answer with firstTrueIndex:
Incorrect (different variant):
while (left < right) { int mid = (left + right) >> 1; if (nums[mid] >= x) { right = mid; } else { left = mid + 1; } } return left;
Correct (template-compliant):
int firstTrueIndex = nums.length; // Default if not found while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] >= x) { firstTrueIndex = mid; right = mid - 1; } else { left = mid + 1; } } return firstTrueIndex;
2. 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 does quick sort divide the problem into subproblems?
Recommended Readings
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
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 If you prefer videos here's a video that explains recursion in a fun and easy way 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 him
Want a Structured Path to Master System Design Too? Don’t Miss This!