933. Number of Recent Calls
Write a class RecentCounter
to count recent requests.
It has only one method: ping(int t)
, where t represents some time in milliseconds.
Return the number of ping
s that have been made from 3000 milliseconds ago until now.
Any ping with time in [t - 3000, t]
will count, including the current ping.
It is guaranteed that every call to ping
uses a strictly larger value of t
than before.
Example:
Input: inputs = ["RecentCounter","ping","ping","ping","ping"], inputs = [[],[1],[100],[3001],[3002]]
Output: [null,1,2,3,3]
Note:
- Each test case will have at most
10000
calls toping
. - Each test case will call
ping
with strictly increasing values oft
. - Each call to ping will have
1 <= t <= 10^9
.
State the problem (as you understand it):
- return the elements within the range [t - 3000, t].
Solve examples that are big enough and are normal cases (manually to figure out the general solution):
- The input 3002 in the example means that we need to return pings with time 100, 3001, and 3002 because 2 calculated from 3002 - 3000 and 3002 are the lower bound and upperbound of the range of elements we can return, respectively. So,
- figure out the lowerbound of the range, that is t - 3000
- return timestamps which are equal to or greater than the lowerbound
Ask clarification questions:
- are we required to remove obsolete timestamps, that is timestamps which occured before t - 3000?
- how many calls to ping will I get?
- will I get random values of t for calls to ping?
- what is the valid range of input for the parameter t?
State assumptions:
- I will get at most
10000
calls toping
- ping will be called with strictly increasing values of t
- each call to ping will have 1 <= t <= 10^9
Consider several test cases (edge cases, base cases):
See if a Data Structure fits the problem:
- use queue to keep timestamps in increasing order and get O(n) time complexity for pings
- use TreeMap to keep timestamps in increasing order and it's ceiling method to get O(log(n)) time complexity for pings
See if an Algorithm fits the problem:
- ad-hoc algorithm
Extract the core problem:
- return elements within the range [t-3000, t] efficiently
Try breaking it down into subproblems:
Explain Potential Solutions (see solutin section):
Other solutions:
- Use TreeSet and it's add() and tailSet() methods. Time complexity is O(log(N)), and space complexity is O(N)
- Use binary search to find the index of the ceiling of t - 3000, then list.size() - index is the answer. Time complexity of O(log(N))
Compare solutions:
- using Queue gives the fastest time complexity since ping() will have an amortised time of O(1)
Follow Up (TODO):
Solution 1:
Ideas:
- keep a queue of the most recent calls in increasing order of
t
- since it is guaranteed that every call to ping uses a strictly larger value of t than before, we don't need any data structure that provides sorting
Steps:
- when we see a new call with time
t
, remove all calls that occurred beforet - 3000
- return queue's size
Validate the correctness (of your pseudocode with a test case):
Data Structure:
- use a queue
Time Complexity:
- O(1), since there will be N calls to ping() we need to analyse the complexity of N calls to ping(). The N calls will have a total complexity of O(N). Why? Because even if the number of iteration of the while loop in the worst case for one call is N, after N calls there will only be N iterations of the while loop in total. This is because at every iteration of the while loop we remove one element of the queue and of course we can only remove maximum N elements in total. I think this analysis is called the aggregate method. So ping() has an amortised time of O(1)
Space Complexity:
- O(W), where W = 3000W=3000 is the size of the window we should scan for recent calls. In this problem, the complexity can be considered O(1)
class RecentCounter {
private Queue<Integer> queue = new LinkedList<>();
public RecentCounter() {
}
public int ping(int t) {
this.queue.add(t);
while (this.queue.peek() < t - 3000) {
this.queue.poll();
}
return this.queue.size();
}
}
Test your code again to make sure you don't have any bugs.
Solution 2:
Ideas:
- use current time and total number of calls as the key and value of TreeMap, respectively
Steps:
- add the given time to the TreeMap
- return the size of a map whose keys are greater than or equal to the t - 3000
Validate the correctness (of your pseudocode with a test case):
Data Structure:
- use a TreeMap
Time Complexity:
- O(log(N))
Space Complexity:
- O(N), where N is the number of ping calls from first one till now.
class RecentCounter {
private Queue<Integer> queue = new LinkedList<>();
public RecentCounter() {
}
public int ping(int t) {
this.queue.add(t);
while (this.queue.peek() < t - 3000) {
this.queue.poll();
}
return this.queue.size();
}
}
Test your code again to make sure you don't have any bugs.
Solution 3:
Ideas:
- use ArrayList to store timestamps
- use binary search to find the index of the ceiling of t - 3000
Steps:
- add the given time to the ArrayList
- use binary search to find the index of the ceiling of t - 3000
- return list's size - index
Validate the correctness (of your pseudocode with a test case):
Data Structure:
- use an ArrayList
Time Complexity:
- O(log(N))
Space Complexity:
- O(N), where N is the number of ping calls from first one till now.
class RecentCounter {
private List<Integer> list = new ArrayList<>();
public RecentCounter() {
}
public int ping(int t) {
this.list.add(t);
int target = t - 3000;
int left = 0;
int right = list.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (list.get(mid) >= target) {
right = mid;
} else {
left = mid + 1;
}
}
return list.size() - left;
}
}
Test your code again to make sure you don't have any bugs.