Count Occurrences of Element in Sorted Array in DSA C++ - Time & Space Complexity
We want to know how fast we can count how many times a number appears in a sorted list.
How does the time needed change when the list gets bigger?
Analyze the time complexity of the following code snippet.
int countOccurrences(const vector<int>& arr, int target) {
int first = lower_bound(arr.begin(), arr.end(), target) - arr.begin();
int last = upper_bound(arr.begin(), arr.end(), target) - arr.begin();
return last - first;
}
This code finds how many times target appears in a sorted array using two binary searches.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Two binary searches (lower_bound and upper_bound).
- How many times: Each binary search repeats steps about log n times, where n is array size.
Each binary search cuts the search space in half repeatedly.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 2 * 4 = 8 steps |
| 100 | About 2 * 7 = 14 steps |
| 1000 | About 2 * 10 = 20 steps |
Pattern observation: Operations grow slowly, increasing by about 1 step each time input size multiplies by 2.
Time Complexity: O(log n)
This means the time to count occurrences grows slowly, even if the array gets much bigger.
[X] Wrong: "Counting occurrences requires checking every element, so it must be O(n)."
[OK] Correct: Because the array is sorted, we can use binary search to jump directly to the target's start and end positions, avoiding checking all elements.
Knowing how to use binary search to count occurrences shows you understand how to use sorting to speed up searches, a key skill in many coding problems.
"What if the array was not sorted? How would the time complexity change?"
Practice
Solution
Step 1: Understand linear vs binary search time
Linear search checks each element one by one, taking O(n) time. Binary search divides the search space in half each time, taking O(log n) time.Step 2: Apply binary search to counting occurrences
Using binary search to find the first and last occurrence of the element reduces the total search time significantly compared to scanning the whole array.Final Answer:
It reduces the search time from linear to logarithmic -> Option AQuick Check:
Binary search = faster search [OK]
- Thinking binary search sorts the array
- Confusing memory usage with speed
- Assuming binary search works on unsorted arrays
Solution
Step 1: Check parameter types and count
The function needs the array (int arr[]), its size (int n), and the target element (int x).Step 2: Check return type
It should return an int index of the first occurrence, so return type is int.Final Answer:
int firstOccurrence(int arr[], int n, int x) -> Option DQuick Check:
Correct parameters and return type [OK]
- Missing array size parameter
- Wrong return type (void instead of int)
- Incorrect array parameter type
int arr[] = {1, 2, 2, 2, 3, 4, 5};
int n = 7;
int x = 2;
int countOccurrences(int arr[], int n, int x) {
int first = -1, last = -1;
int low = 0, high = n - 1;
// Find first occurrence
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == x) {
first = mid;
high = mid - 1;
} else if (arr[mid] < x) {
low = mid + 1;
} else {
high = mid - 1;
}
}
if (first == -1) return 0;
low = first; high = n - 1;
// Find last occurrence
while (low <= high) {
int mid = low + (high - low) / 2;
if (arr[mid] == x) {
last = mid;
low = mid + 1;
} else if (arr[mid] < x) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return last - first + 1;
}
std::cout << countOccurrences(arr, n, x) << std::endl;Solution
Step 1: Find first occurrence of 2
Binary search finds first 2 at index 1.Step 2: Find last occurrence of 2
Binary search finds last 2 at index 3.Step 3: Calculate count
Count = last - first + 1 = 3 - 1 + 1 = 3.Final Answer:
3 -> Option AQuick Check:
Occurrences of 2 = 3 [OK]
- Returning last index instead of count
- Not updating low and high correctly
- Returning 0 when element exists
int countOccurrences(int arr[], int n, int x) {
int first = -1, last = -1;
int low = 0, high = n - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (arr[mid] == x) {
first = mid;
high = mid - 1;
} else if (arr[mid] < x) {
low = mid + 1;
} else {
high = mid - 1;
}
}
if (first == -1) return 0;
low = first; high = n - 1;
while (low < high) {
int mid = low + (high - low) / 2;
if (arr[mid] == x) {
last = mid;
low = mid + 1;
} else if (arr[mid] < x) {
low = mid + 1;
} else {
high = mid - 1;
}
}
return last - first + 1;
}Solution
Step 1: Analyze loop condition
The loops use 'while (low < high)', which misses checking when low equals high, possibly skipping the last element.Step 2: Correct loop condition
Changing to 'while (low <= high)' ensures all elements are checked, including the last one.Final Answer:
Using 'while (low < high)' instead of 'while (low <= high)' causes missing last element -> Option BQuick Check:
Loop condition must include equality [OK]
- Using < instead of <= in loops
- Not updating first and last correctly
- Miscomputing mid index
Solution
Step 1: Understand repeated binary search cost
Running binary search for each element separately costs O(m log n) for m queries, which can be expensive.Step 2: Use frequency map for efficiency
A single pass over the sorted array can build a frequency map (element to count) in O(n). Then queries are O(1) each.Final Answer:
Precompute a frequency map using a single pass, then query counts in O(1) -> Option CQuick Check:
Frequency map = fast multiple queries [OK]
- Running binary search repeatedly for each query
- Sorting array multiple times unnecessarily
- Using linear search for each query
Ready to prove it?
Pass the test and this pattern moves unknown
