Count Occurrences of Element in Sorted Array in DSA Rust - 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.
fn count_occurrences(arr: &[i32], target: i32) -> usize {
fn find_first(arr: &[i32], target: i32) -> Option {
let mut left = 0;
let mut right = arr.len();
while left < right {
let mid = (left + right) / 2;
if arr[mid] < target {
left = mid + 1;
} else {
right = mid;
}
}
if left < arr.len() && arr[left] == target { Some(left) } else { None }
}
fn find_last(arr: &[i32], target: i32) -> Option {
let mut left = 0;
let mut right = arr.len();
while left < right {
let mid = (left + right) / 2;
if arr[mid] <= target {
left = mid + 1;
} else {
right = mid;
}
}
if right > 0 && arr[right - 1] == target { Some(right - 1) } else { None }
}
match (find_first(arr, target), find_last(arr, target)) {
(Some(first), Some(last)) => last - first + 1,
_ => 0,
}
}
This code finds the first and last place of the target number using binary search, then counts how many times it appears.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Two binary search loops to find first and last positions.
- How many times: Each binary search loop runs about log₂(n) times, where n is the array size.
As the list gets bigger, the number of steps grows slowly because each search cuts the list in half.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 2 x 4 = 8 steps |
| 100 | About 2 x 7 = 14 steps |
| 1000 | About 2 x 10 = 20 steps |
Pattern observation: The steps grow slowly, roughly doubling the input size adds only one more step per search.
Time Complexity: O(log n)
This means the time to count occurrences grows slowly as the list gets bigger, making it very efficient.
[X] Wrong: "Counting occurrences requires checking every element one by one, so it must be O(n)."
[OK] Correct: Because the list is sorted, we can use binary search to jump directly to the target's start and end, 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 challenges.
"What if the array was not sorted? How would the time complexity change?"
Practice
Solution
Step 1: Understand binary search efficiency
Binary search divides the search space in half each time, making it very fast on sorted arrays.Step 2: Compare with linear search
Linear search checks each element, which is slower (linear time). Binary search is faster (logarithmic time).Final Answer:
It reduces the search time to logarithmic complexity -> Option CQuick Check:
Binary search = O(log n) time [OK]
- Thinking binary search sorts the array
- Assuming binary search works on unsorted arrays
- Confusing counting with searching
target in a sorted slice arr?Solution
Step 1: Check parameter types
Using a slice&[i32]is efficient for read-only access without ownership transfer.Step 2: Check return type
Counting occurrences returns a non-negative number, sousizeis appropriate.Final Answer:
fn count_occurrences(arr: &[i32], target: i32) -> usize { /* code */ } -> Option AQuick Check:
Slice + usize return = correct signature [OK]
- Using Vec when slice is enough
- Returning i32 instead of usize for counts
- Confusing ownership with borrowing
fn main() {
let arr = [1, 2, 3, 3, 3, 3, 5];
let count = count_occurrences(&arr, 3);
println!("{}", count);
}
fn count_occurrences(arr: &[i32], target: i32) -> usize {
let first = arr.binary_search(&target).unwrap_or_else(|x| x);
let mut count = 0;
for &num in &arr[first..] {
if num == target {
count += 1;
} else {
break;
}
}
count
}Solution
Step 1: Find occurrence index
binary_search returns Ok(index) for 3 at index 2.Step 2: Count consecutive 3s from index 2
Elements at indices 2,3,4,5 are 3, so count = 4.Final Answer:
4 -> Option AQuick Check:
4 occurrences counted correctly [OK]
- Using unwrap_or_else returns insertion point, not found index
- Counting beyond target elements
- Not breaking loop on different element
target in a sorted array arr:
fn count_occurrences(arr: &[i32], target: i32) -> usize {
let start = arr.binary_search(&target).unwrap_or_else(|_| 0);
let mut count = 0;
for &num in &arr[start..] {
if num == target {
count += 1;
} else {
break;
}
}
count
}Solution
Step 1: Understand unwrap_or_else behavior
unwrap_or_else(|_| 0) returns 0 if target not found, which is incorrect start index.Step 2: Consequence of wrong start index
Counting from 0 when target is absent counts wrong elements, giving wrong count.Final Answer:
unwrap_or_else(|_| 0) returns 0 even if target not found, causing wrong count -> Option DQuick Check:
unwrap_or_else should use insertion point or handle not found [OK]
- Using unwrap_or_else(|| 0)
- Not breaking loop on different element
- Confusing first and last occurrence index
arr with possible duplicates, which Rust approach best counts occurrences of target efficiently and correctly for all cases, including when target is absent?
Options:
A) Use binary_search to find insertion points of target and target+1, then subtract indices.
B) Use binary_search to find any occurrence, then count forward until different element.
C) Use linear scan from start to end counting matches.
D) Use binary_search to find last occurrence, then count backward until different element.Solution
Step 1: Understand insertion points method
Finding insertion points for target and target+1 gives start and end indices of target range.Step 2: Calculate count by subtraction
Subtracting these indices gives exact count, even if target is absent (count = 0).Step 3: Compare with other methods
Counting forward or backward after binary_search can fail if target not found; linear scan is slow.Final Answer:
Find insertion points for target and target+1, subtract to get count -> Option BQuick Check:
Insertion points subtraction = accurate count [OK]
- Counting forward without checking if target found
- Using linear scan on large arrays
- Counting backward without binary_search for last occurrence
Ready to prove it?
Pass the test and this pattern moves unknown
