|
| 1 | +from collections import deque |
| 2 | + |
| 3 | + |
| 4 | +class RecentCounter: |
| 5 | + """ |
| 6 | + Your RecentCounter object will be instantiated and called as such: |
| 7 | + obj = RecentCounter() |
| 8 | + param_1 = obj.ping(t) |
| 9 | +
|
| 10 | + Example: |
| 11 | + Input |
| 12 | + ["RecentCounter", "ping", "ping", "ping", "ping"] |
| 13 | + [[], [1], [100], [3001], [3002]] |
| 14 | + Output |
| 15 | + [null, 1, 2, 3, 3] |
| 16 | + """ |
| 17 | + |
| 18 | + def __init__(self): |
| 19 | + self.ping_ts = [] |
| 20 | + self.length = 0 |
| 21 | + |
| 22 | + def ping(self, t: int) -> int: |
| 23 | + # At any instant of time, self.ping_ts has maximum length |
| 24 | + # of 3001. So time complexity is O(1) and space complexity |
| 25 | + # is O(1). |
| 26 | + self.ping_ts.append(t) |
| 27 | + |
| 28 | + i, count = self.length, 0 |
| 29 | + while i >= 0 and self.ping_ts[i] >= t - 3000: |
| 30 | + count, i = count + 1, i - 1 |
| 31 | + |
| 32 | + self.ping_ts, self.length = self.ping_ts[i + 1 :], count |
| 33 | + |
| 34 | + return count |
| 35 | + |
| 36 | + |
| 37 | +class RecentCounterOfficial: |
| 38 | + """ |
| 39 | + == Overview == |
| 40 | + We are given a sequence of ping calls, i.e. [t1, t2, ... tn], ordered by the |
| 41 | + chronological order of their arrival time. |
| 42 | + Given the current ping call ti, we are asked to count the number of previous calls |
| 43 | + that fall in the range [ti - 3000, ti]. |
| 44 | +
|
| 45 | + == Approach 1: Iteration over Sliding Window == |
| 46 | + The idea is that we can use a container such as array or list to keep track of all |
| 47 | + the incoming ping calls. At each occasion of ping(t) call, first we append the call |
| 48 | + to the container, and then starting from the current call, we iterate backwards to |
| 49 | + count the calls that fall into the time range of [t-3000, t]. |
| 50 | +
|
| 51 | + Once the ping calls become outdated, i.e. out of the scope of [t - 3000, t], we do |
| 52 | + not need to keep them any longer in the container, since they will not contribute |
| 53 | + to the solution later. |
| 54 | + As a result, one optimization that we could do is that rather than keeping all the |
| 55 | + historical ping calls in the container, we could remove the outdated calls on the |
| 56 | + go, which can avoid overflow of the container and reduce the memory consumption to |
| 57 | + the least. |
| 58 | +
|
| 59 | + In summary, out container will function like a sliding window over the ever- |
| 60 | + growing sequence of ping calls. |
| 61 | +
|
| 62 | + == Algorithm == |
| 63 | + To implement the sliding window, we could use deque in Python. |
| 64 | + Then the ping(t) function can be implemented in two steps: |
| 65 | + - Step 1: We append the current ping call to the tail of the sliding window. |
| 66 | + - Step 2: Starting from the head of the sliding window, we remove the outdated |
| 67 | + calls, until we come across a still valid ping call. |
| 68 | + As a result, the remaining calls in the sliding window are the ones that fall into |
| 69 | + the range [t-3000,t]. |
| 70 | +
|
| 71 | + == Complexity Analysis == |
| 72 | + It is guaranteed that every call to ping uses a strictly larger value of t than |
| 73 | + before. Based on the above condition, the maximal number of elements in our sliding |
| 74 | + window would be 3000, which is also the maximal time difference between the head |
| 75 | + and the tail elements. |
| 76 | +
|
| 77 | + - Time Complexity: O(1) |
| 78 | + - The main time complexity of our ping(t) function lies in the loop, which |
| 79 | + in the worst case would run 3000 iterations to pop out all outdated elements |
| 80 | + and in the best case a single iteration. |
| 81 | + - Therefore, for a single invocation of ping() functions, its |
| 82 | + time complexity if O(3000) = O(1). |
| 83 | + - If we assume that there is a ping call at each timestamp, then the cost of |
| 84 | + ping() is further amortized, where at each invocation, we would only need to |
| 85 | + pop out a single element, once the sliding window reaches its upper bound. |
| 86 | + - Space Complexity: O(1) |
| 87 | + - As we estimated before, the maximal size of our sliding window is 3000, |
| 88 | + which is a constant. |
| 89 | + """ |
| 90 | + |
| 91 | + def __init__(self): |
| 92 | + self.slide_window = deque() |
| 93 | + self.length = 0 |
| 94 | + |
| 95 | + def ping(self, t: int) -> int: |
| 96 | + self.slide_window.append(t) |
| 97 | + self.length += 1 |
| 98 | + |
| 99 | + while self.slide_window[0] < t - 3000: |
| 100 | + self.slide_window.popleft() |
| 101 | + self.length -= 1 |
| 102 | + |
| 103 | + return self.length |
0 commit comments