Number of Recent Calls
Problem
RecentCounter. Each call ping(t) records a request
at time t milliseconds and returns how many recorded requests (including this
one) fall in the inclusive window [t - 3000, t]. Every t is
strictly larger than the previous one.ping(1) ping(100) ping(3001) ping(3002)Output1, 2, 3, 3ping(5) ping(3006)Output1, 11 ≤ t ≤ 109 · t strictly increasing · ≤ 104 calls
Try it yourself
class RecentCounter {
public RecentCounter() {
}
public int ping(int t) {
}
}
Instinct Arrival order matters, or you need the max/min of a moving window - reach for a FIFO queue, or a deque of indices kept in sorted order.
Memory hack Old ones leave by the front door, weak ones get bounced out the back - so whoever stands at the front is always the champion.
Open the Queue & Monotonic Deque guide →Approaches
Brute ForceStore All, Count by Scanning
O(n) per pingSpace O(n)Append every timestamp to a list and, on each ping, count how many stored
times are at least t - 3000. Nothing is ever thrown away.
class RecentCounter { private final List<Integer> times = new ArrayList<>(); public RecentCounter() { } public int ping(int t) { times.add(t); int count = 0; for (int time : times) { if (time >= t - 3000) count++; } return count; } }
Trivially correct, but each ping rescans the whole history.
BetterSorted History + Binary Search
O(log n) per pingSpace O(n)The stored times are already sorted because t increases. Binary
search for the first time ≥ t - 3000; everything from there to the end
is inside the window.
class RecentCounter { private final List<Integer> times = new ArrayList<>(); public RecentCounter() { } public int ping(int t) { times.add(t); int lo = 0, hi = times.size() - 1; // find first index with time >= t - 3000 while (lo < hi) { int mid = (lo + hi) >>> 1; if (times.get(mid) < t - 3000) lo = mid + 1; else hi = mid; } return times.size() - lo; } }
Fast queries, but memory still grows with every call ever made.
OptimalSliding Window Queue
O(1) amortized per pingSpace O(W), W ≤ 3001Push t to the back, then pop from the front while the oldest time
is below t - 3000. The queue then holds exactly the window, so its size
is the answer. Each timestamp is pushed and popped once.
class RecentCounter { private final Deque<Integer> window = new ArrayDeque<>(); public RecentCounter() { } public int ping(int t) { window.offerLast(t); while (window.peekFirst() < t - 3000) window.pollFirst(); // expired for good return window.size(); } }
Amortized O(1) and memory bounded by the window — the expected answer.
At a glance
| Level | Approach | Time | Space |
|---|---|---|---|
| Brute Force | Store All, Count by Scanning | O(n) per ping | O(n) |
| Better | Sorted History + Binary Search | O(log n) per ping | O(n) |
| Optimal | Sliding Window Queue | O(1) amortized per ping | O(W), W ≤ 3001 |
- Using
≤instead of<when evicting —t - 3000itself counts. - Checking the window before adding
t— the current ping must be counted. - Comparing
Integerobjects with==instead of unboxing.
- Ask whether times are increasing — that is what makes a plain queue enough.
- Point out the brute force keeps data it can never use again.
- Explain the amortized cost: every element is enqueued and dequeued once.