🧠
Binary Search for Left and Right Boundaries
💡 This approach leverages the sorted property to find the first and last occurrence of the target efficiently, reducing time complexity drastically.
Intuition
Use binary search twice: once to find the leftmost index of the target, and once to find the rightmost index. The count is the difference plus one.
Algorithm
- Define a helper function to find the left boundary (first occurrence) of the target using binary search.
- Define a helper function to find the right boundary (last occurrence) of the target using binary search.
- If the target is not found, return 0.
- Otherwise, return right boundary - left boundary + 1 as the count.
💡 The tricky part is adjusting binary search to find boundaries instead of just any occurrence.
def left_boundary(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
def right_boundary(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = left + (right - left) // 2
if nums[mid] <= target:
left = mid + 1
else:
right = mid - 1
return right
def count_occurrences(nums, target):
left = left_boundary(nums, target)
right = right_boundary(nums, target)
if left <= right:
return right - left + 1
return 0
# Driver code
if __name__ == '__main__':
nums = [1,2,2,2,3,4,5]
target = 2
print(count_occurrences(nums, target)) # Output: 3
Line Notes
left, right = 0, len(nums) - 1Initialize search boundaries
mid = left + (right - left) // 2Calculate mid to avoid overflow
if nums[mid] < target:Move left pointer if mid value less than target
else: right = mid - 1Move right pointer to narrow left boundary
if nums[mid] <= target:Adjust left pointer to find right boundary
return rightReturn index of right boundary
if left <= right:Check if target exists in array
return right - left + 1Calculate count from boundaries
public class CountOccurrences {
private static int leftBoundary(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
private static int rightBoundary(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
public static int countOccurrences(int[] nums, int target) {
int left = leftBoundary(nums, target);
int right = rightBoundary(nums, target);
if (left <= right) {
return right - left + 1;
}
return 0;
}
public static void main(String[] args) {
int[] nums = {1,2,2,2,3,4,5};
int target = 2;
System.out.println(countOccurrences(nums, target)); // Output: 3
}
}
Line Notes
int left = 0, right = nums.length - 1;Initialize binary search boundaries
int mid = left + (right - left) / 2;Calculate mid index safely
if (nums[mid] < target)Shift left pointer if mid less than target
else { right = mid - 1; }Shift right pointer to find left boundary
if (nums[mid] <= target)Shift left pointer to find right boundary
return right;Return right boundary index
if (left <= right)Check if target exists in array
return right - left + 1;Calculate count from boundaries
#include <iostream>
#include <vector>
using namespace std;
int leftBoundary(const vector<int>& nums, int target) {
int left = 0, right = (int)nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
int rightBoundary(const vector<int>& nums, int target) {
int left = 0, right = (int)nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
int countOccurrences(const vector<int>& nums, int target) {
int left = leftBoundary(nums, target);
int right = rightBoundary(nums, target);
if (left <= right) {
return right - left + 1;
}
return 0;
}
int main() {
vector<int> nums = {1,2,2,2,3,4,5};
int target = 2;
cout << countOccurrences(nums, target) << endl; // Output: 3
return 0;
}
Line Notes
int left = 0, right = (int)nums.size() - 1;Initialize search boundaries
int mid = left + (right - left) / 2;Calculate mid index safely
if (nums[mid] < target)Move left pointer if mid less than target
else { right = mid - 1; }Move right pointer to narrow left boundary
if (nums[mid] <= target)Adjust left pointer to find right boundary
return right;Return right boundary index
if (left <= right)Check if target exists
return right - left + 1;Calculate count from boundaries
function leftBoundary(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
function rightBoundary(nums, target) {
let left = 0, right = nums.length - 1;
while (left <= right) {
let mid = left + Math.floor((right - left) / 2);
if (nums[mid] <= target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return right;
}
function countOccurrences(nums, target) {
const left = leftBoundary(nums, target);
const right = rightBoundary(nums, target);
if (left <= right) {
return right - left + 1;
}
return 0;
}
// Test
const nums = [1,2,2,2,3,4,5];
const target = 2;
console.log(countOccurrences(nums, target)); // Output: 3
Line Notes
let left = 0, right = nums.length - 1;Initialize binary search boundaries
let mid = left + Math.floor((right - left) / 2);Calculate mid index safely
if (nums[mid] < target)Move left pointer if mid less than target
else { right = mid - 1; }Move right pointer to narrow left boundary
if (nums[mid] <= target)Adjust left pointer to find right boundary
return right;Return right boundary index
if (left <= right)Check if target exists
return right - left + 1;Calculate count from boundaries
Each binary search runs in logarithmic time, and we do two searches, so total time is O(log n). Space is constant.
💡 For n=100,000, this means about 2 * 17 = 34 operations, which is very fast compared to linear scanning.
Interview Verdict: Accepted and efficient
This is the preferred approach in interviews for sorted arrays because it is both correct and efficient.