1352. Product of the Last K Numbers - Detailed Explanation
Problem Statement:
Design a class that processes a stream of integers and can return the product of the last k
numbers quickly. You need to implement a ProductOfNumbers
class with the following functions:
-
ProductOfNumbers()
: Initializes the object with an empty stream. -
add(int num)
: Appends the integernum
to the stream of numbers. -
getProduct(int k)
: Returns the product of the lastk
numbers in the current stream. (You can assumek
is always <= the length of the current stream.)
Important: The test data is set up so that the product of any contiguous sequence of numbers (at any time) will fit in a 32-bit integer (no overflow issues).
Examples:
-
Example 1:
Input:
["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"]
[[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]
Output:
[null,null,null,null,null,null,20,40,0,null,32]
Explanation:-
ProductOfNumbers productOfNumbers = new ProductOfNumbers()
– Initializes an empty stream (output isnull
for constructor). -
productOfNumbers.add(3)
– Stream becomes[3]
. -
productOfNumbers.add(0)
– Stream becomes[3, 0]
. -
productOfNumbers.add(2)
– Stream becomes[3, 0, 2]
. -
productOfNumbers.add(5)
– Stream becomes[3, 0, 2, 5]
. -
productOfNumbers.add(4)
– Stream becomes[3, 0, 2, 5, 4]
. -
productOfNumbers.getProduct(2)
– Returns20
. (The last 2 numbers are 5 and 4, and 5×4 = 20) -
productOfNumbers.getProduct(3)
– Returns40
. (The last 3 numbers are 2, 5, 4, and 2×5×4 = 40) -
productOfNumbers.getProduct(4)
– Returns0
. (The last 4 numbers are 0, 2, 5, 4, and 0×2×5×4 = 0 due to the zero) -
productOfNumbers.add(8)
– Stream becomes[3, 0, 2, 5, 4, 8]
. -
productOfNumbers.getProduct(2)
– Returns32
. (The last 2 numbers are 4 and 8, and 4×8 = 32)
-
-
Example 2: (No zeros in the stream)
Input:
["ProductOfNumbers","add","add","add","getProduct","getProduct"]
[[],[7],[3],[4],[3],[1]]
Output:
[null,null,null,null,84,4]
Explanation:- After adding 7, 3, 4, the stream is
[7, 3, 4]
. getProduct(3)
returns 84 (7×3×4).getProduct(1)
returns 4 (the last number, which is 4).
- After adding 7, 3, 4, the stream is
-
Example 3: (Multiple zeros in the stream)
Input:
["ProductOfNumbers","add","add","getProduct","add","getProduct","add","getProduct"]
[[],[0],[0],[2],[5],[1],[4],[4]]
Output:
[null,null,null,0,null,5,null,0]
Explanation:-
After
add(0)
twice, the stream is[0, 0]
. Any product of the last 2 numbers here is 0. -
getProduct(2)
returns 0 (last 2 numbers are 0 and 0). -
After
add(5)
, stream is[0, 0, 5]
. -
getProduct(1)
returns 5 (the last number is 5). -
After
add(4)
, stream is[0, 0, 5, 4]
. -
getProduct(4)
returns 0 (the last 4 numbers include two zeros, making the product 0).
-
Constraints:
-
0 <= num <= 100
(The values to add are between 0 and 100, inclusive.) -
1 <= k <= 4 * 10^4
(You will be asked for up to 40,000 last numbers in the product query.) -
At most
4 * 10^4
calls in total will be made toadd
andgetProduct
(so up to 40,000 operations overall). -
The product of any contiguous subsequence will fit in a 32-bit integer (no overflow).
Hints:
-
Hint 1: A straightforward solution is to multiply the last
k
numbers each time you callgetProduct
. But consider the worst-case scenario: if there are many operations andk
is large, repeatedly multiplyingk
numbers could be slow. Can you avoid doing redundant multiplication for every query? Think about ways to reuse previous computation results. -
Hint 2: One common technique to answer range queries quickly is using prefix sums. Similarly, think in terms of prefix products. If you had a running product of the numbers as you add them, could you use it to get the product of the last
k
numbers without multiplying allk
individually? -
Hint 3: Zero is a special case for multiplication. If one of the last
k
numbers is 0, the product should be 0. How can you efficiently detect if a zero is in the lastk
numbers? Consider what happens to your prefix product idea when a zero is added to the stream (you might need to adjust or reset some stored values whennum = 0
).
Multiple Approaches:
We will discuss two approaches to solve this problem: a brute-force approach and an optimized prefix-product approach. We’ll explain each step-by-step and walk through how they work with examples.
1. Brute Force Approach:
Idea: Keep all numbers in a list as they are added. For each getProduct(k)
query, compute the product of the last k
elements by iterating through those elements.
-
How it works: In the
add(num)
method, simply appendnum
to a list (stream
). ForgetProduct(k)
, start from the end of the list and multiply the lastk
numbers one by one. -
Step-by-step (Example): Suppose the stream is
[2, 5, 4]
(added in that order). If we callgetProduct(2)
, the brute-force method would multiply the last 2 elements: 5 × 4 = 20. If we then callgetProduct(3)
, it would multiply the last 3 elements: 2 × 5 × 4 = 40. Every time we callgetProduct
, we loop through the requested segment of the list and multiply. -
Visual Walk-through (Brute force):
Let’s simulate Example 1 with the brute force approach:-
After adding
[3, 0, 2, 5, 4]
, our stored list is[3, 0, 2, 5, 4]
. -
getProduct(2)
: loop through the last 2 elements (which are 5 and 4) and multiply: 5×4 = 20. -
getProduct(3)
: loop through the last 3 elements (2, 5, 4) and multiply: 2×5×4 = 40. -
getProduct(4)
: loop through the last 4 elements (0, 2, 5, 4) and multiply: 0×2×5×4 = 0 (at the first element 0, you might already know the result will be 0). -
Every
getProduct
requires iterating over k elements in the worst case.
-
-
Drawbacks: This approach is straightforward, but it can be slow. In the worst case, if you have
n
numbers and each query asks for the product of almost all of them (k
is large), each query takes O(k
) time. With up to 40,000 operations, this could be too slow if many queries multiply 40,000 numbers each. We need a more efficient way to handle repeated product queries.
2. Optimized Approach: Prefix Products and Zero Handling:
Idea: Use a prefix product array to get the product of the last k
numbers in O(1) time. The trick is to store the cumulative product of the numbers as we go, and update it intelligently when zeros appear.
-
Prefix Product Concept: We maintain an array
prefixProducts
where each element is the product of all numbers in the stream up to that point since the last zero. We also include a convenient initial value1
at the start of this array (as a neutral element for multiplication). This way, the product of a range of elements can be obtained by dividing two prefix products. -
Handling Zeroes: If the new number
num
added is0
, it resets the multiplication chain because any product that includes this zero should be zero. We handle this by clearing the prefixProducts array and starting fresh after the zero. In practice, whennum = 0
, we resetprefixProducts
to[1]
(which represents an empty product starting after the zero). This means that anygetProduct(k)
that tries to include this zero (by looking further back than the last reset) will be detected because the length of the prefix array is shorter thank+1
in that case. -
How to get product of last k using prefix products: Whenever a new number is added and is non-zero, we compute the new prefix product as
prefixProducts[-1] * num
(multiply the last stored product by the new number) and append it. Suppose after some operations,prefixProducts
looks like[1, p1, p2, ..., pn]
, where eachpi
is the product up to that point since the last zero. Let the current last prefix product beP_last = prefixProducts[-1]
, which equals the product of all numbers in the current segment (since the last zero). Now, to get the product of the lastk
numbers, we need the product of a suffix of lengthk
from the stream:-
Let
P_before = prefixProducts[-1 - k]
be the prefix product just before those lastk
numbers. -
Then the product of the last
k
numbers isP_last / P_before
. (BecauseP_last
is product of everything up to current, andP_before
is product of everything up to just before the last k numbers; dividing gives the product of that range.) -
If
k
is larger than the current prefix length minus 1 (minus 1 because of the initial 1), that means the query is asking for a range that extends past the last zero. In this case, we return 0 (because a zero was encountered within that range). We can check this quickly by comparingk
with the size ofprefixProducts
.
-
-
Step-by-step (Example): Consider building the prefix products for Example 1:
-
Start with
prefixProducts = [1]
(initial dummy product). -
add(3)
: 3 is not zero, so appendprefixProducts[-1] * 3 = 1 * 3 = 3
. NowprefixProducts = [1, 3]
(this represents: product up to first element is 3). -
add(0)
: 0 resets everything. Instead of multiplying, we resetprefixProducts = [1]
. (We don't keep the old prefix values since anything before the zero is now irrelevant for future products.) -
add(2)
: 2 is non-zero, so append1 * 2 = 2
. NowprefixProducts = [1, 2]
(this segment is for the sub-stream after the zero). -
add(5)
: append2 * 5 = 10
. NowprefixProducts = [1, 2, 10]
. (Meaning: for the sub-stream after the last zero, the product of [2,5] is 10.) -
add(4)
: append10 * 4 = 40
. NowprefixProducts = [1, 2, 10, 40]
. (Product of [2,5,4] is 40.) -
Now, to answer queries:
getProduct(2)
: We need product of last 2 numbers (which are 5 and 4). The prefix array currently[1, 2, 10, 40]
corresponds to the segment [2,5,4]. For last 2, we takeP_last = 40
(product of [2,5,4]) andP_before = prefixProducts[-1-2] = prefixProducts[-3]
. HereprefixProducts[-3]
is the element at index 1 (0-based) which is 2. Dividing: 40 / 2 = 20. This matches the expected product 5×4.getProduct(3)
: Last 3 numbers (2,5,4). Now we takeP_last = 40
andP_before = prefixProducts[-1-3] = prefixProducts[-4]
. That is the element at index 0 which is 1. So 40 / 1 = 40, as expected.getProduct(4)
: Last 4 numbers (0,2,5,4). Now, noticek = 4
. The currentprefixProducts
length is 4 (indices 0 to 3). We check and see thatk (4) >= len(prefixProducts) (4)
. This means the query is asking for a range that extends beyond the last zero (because our prefix array only has information for 3 numbers after the last zero, plus the initial 1). Therefore, we return 0 immediately without any division. This correctly reflects that one of those last 4 numbers was a zero, making the product 0.
-
add(8)
: 8 is non-zero, so append to prefix: new product = 40 * 8 = 320. NowprefixProducts = [1, 2, 10, 40, 320]
(product of [2,5,4,8] is 320). -
getProduct(2)
: Last 2 numbers (4,8). NowP_last = 320
andP_before = prefixProducts[-1-2] = prefixProducts[-3] = 10
. 320 / 10 = 32. Correct result (4×8).
-
-
Why this works: By resetting on zero, we ensure that
prefixProducts
always reflects the product of the current contiguous segment of non-zero numbers. If a query asks for a product that goes beyond the start of this segment (meaning it would include a zero), we detect it becausek
will be larger than the length of this segment. All operations in this approach are O(1): each add is just a multiplication (or reset), and each query is just a division (or a quick check and maybe a division). There’s no heavy looping in queries. -
Alternate perspective (zero-index tracking): Another way to think of zero handling is to keep track of the index of the last zero in the stream. If you know the last zero’s position, you can compare it to the range of indices you need for the last k numbers. If the last zero index is within that range, you return 0. The prefix product approach above essentially does the same thing implicitly by resetting.
Code Implementations:
Brute Force Approach – Python:
Brute Force Approach – Java:
Optimized Prefix-Product Approach – Python:
Optimized Prefix-Product Approach – Java:
Explanation of the code: In both Python and Java optimized versions, the logic is the same:
- We use a list (
prefixProducts
orprefix
) to store cumulative products. It starts with[1]
. - On
add(num)
: ifnum
is zero, we reset the list to[1]
. Ifnum
is non-zero, we appendlast_element * num
to the list. - On
getProduct(k)
: ifk
is at least the length of the prefix list, we return 0 (meaning a zero was encountered in that range). Otherwise, we do the division of the last prefix by the prefix value fromk
elements ago to get the product of the last k numbers.
Complexity Analysis:
-
Brute Force Approach:
-
Time Complexity:
add()
is O(1) (just appending to a list).getProduct(k)
is O(k) because it iterates through k elements to compute the product. In the worst case,k
could be as large as the number of elements in the stream. If we haven
operations and many of them aregetProduct
of large segments, the worst-case time could be on the order of O(n * k). For example, in the worst scenario with 40,000 operations, eachgetProduct(40000)
would multiply 40,000 numbers – this would be extremely slow in a language like Python (and even in Java it could be problematic). -
Space Complexity: O(n) to store all the numbers in the stream (where n is the total numbers added).
-
-
Optimized Prefix-Product Approach:
- Time Complexity: Both
add()
andgetProduct()
are O(1) on average. Eachadd
does a constant amount of work (multiplication and append, or resetting the list). EachgetProduct
does a constant amount of work (just a couple of array index lookups and one division). This holds true even for largek
because we aren’t looping through those k elements, we’re using the stored prefix products. (One subtle detail: if you add a lot of zeros, each zero causes a reset of the prefix list. However, each addition operation is still constant time; we are not carrying out any long loops during resets, just reinitializing a list.) - Space Complexity: O(n) in the worst case, where n is the number of elements added. In the worst scenario (no zeros at all), the
prefixProducts
list will store a prefix product for every element added (plus the initial 1). If zeros occur, the space might be slightly less because we reset, but overall, we still at most store one prefix value per element of the stream. The storage is proportional to the number of elements added (max 40,000). This is efficient for the given input size.
- Time Complexity: Both
Why O(1) queries matter: The optimized approach ensures that even if we have many queries asking for very large segments, each query is fast. This makes the solution efficient even when k
is large or when we have many operations. The brute force approach would not scale well in those cases.
Step-by-Step Walkthrough (Optimized Solution):
Let’s do a detailed walkthrough of the optimized solution using Example 1 ([3, 0, 2, 5, 4, 8]
with queries in between) to see how the internal state changes after each operation:
-
Start:
prefixProducts = [1]
(starts with 1 as a neutral product, representing an "empty" product). -
Operation 1:
add(3)
- New number is 3 (non-zero).
- Compute new prefix = last prefix * 3 = 1 * 3 = 3.
- Append it:
prefixProducts = [1, 3]
. - (This means the product of the last 1 number is 3, which matches the stream [3].)
-
Operation 2:
add(0)
- New number is 0.
- Reset:
prefixProducts = [1]
. - (We reset because any product that includes this 0 will be 0. After this reset, the prefixProducts now only accounts for numbers after the zero. The stream so far is [3, 0], but we dropped the product for the prefix that included 3 because it’s irrelevant for any segment after the 0.)
-
Operation 3:
add(2)
- New number is 2 (non-zero).
- New prefix = 1 * 2 = 2.
prefixProducts = [1, 2]
.- (Now the stream after the last zero is [2], and the product of [2] is 2.)
-
Operation 4:
add(5)
- New number is 5.
- New prefix = 2 * 5 = 10.
prefixProducts = [1, 2, 10]
.- (The stream after the last zero is [2, 5], product is 10.)
-
Operation 5:
add(4)
- New number is 4.
- New prefix = 10 * 4 = 40.
prefixProducts = [1, 2, 10, 40]
.- (The stream after the last zero is [2, 5, 4], product is 40.)
-
Operation 6:
getProduct(2)
– product of last 2 numbers.- We check
k=2
againstlen(prefixProducts)=4
. Since 2 < 4, we have enough data. P_last = prefixProducts[-1] = 40
(product of [2,5,4]).P_before = prefixProducts[-1 - 2] = prefixProducts[-3] = 2
(this is the product of [2], which is the part before the last 2 numbers in the current segment).- Result = 40 / 2 = 20.
- (The last 2 numbers in the stream are [5,4]. Indeed 5×4 = 20.)
- We check
-
Operation 7:
getProduct(3)
– product of last 3 numbers.- Check
k=3
vslen(prefixProducts)=4
. 3 < 4, okay. P_last = 40
.P_before = prefixProducts[-1 - 3] = prefixProducts[-4] = 1
(this is the initial 1, representing product before the current segment start).- Result = 40 / 1 = 40.
- (Last 3 numbers are [2,5,4]; 2×5×4 = 40.)
- Check
-
Operation 8:
getProduct(4)
– product of last 4 numbers.- Check
k=4
vslen(prefixProducts)=4
. Here 4 >= 4, which means we’re asking for a segment length that is not fully covered by our current prefix segment length. In fact,k=4
would include one number prior to the last zero (since our current segment after the last zero has only 3 numbers). - Since
k >= len(prefixProducts)
, we return 0 without further calculation. - (This correctly matches the fact that the last 4 numbers [0,2,5,4] include a zero, so the product is 0.)
- Check
-
Operation 9:
add(8)
- New number is 8.
- Current last prefix was 40, so new prefix = 40 * 8 = 320.
prefixProducts = [1, 2, 10, 40, 320]
.- (The segment after the last zero is now [2,5,4,8], product 320.)
-
Operation 10:
getProduct(2)
– product of last 2 numbers.- Check
k=2
vslen(prefixProducts)=5
. 2 < 5, good. P_last = 320
.P_before = prefixProducts[-1 - 2] = prefixProducts[-3] = 10
.- Result = 320 / 10 = 32.
- (Last 2 numbers are [4,8]; 4×8 = 32.)
- Check
Through this walkthrough, you can see how we only ever do a single multiplication when adding a number (or a reset if it’s zero), and a single division when answering a query (plus a check for zero case). This is why the optimized solution is efficient.
Common Mistakes & Edge Cases:
-
Forgetting to handle zero properly: The most common pitfall is to use a prefix product approach but not reset on zero. If you kept multiplying even when a zero appears, the prefix product would become 0 and remain 0 for all subsequent elements, which breaks the logic for future queries. It’s crucial to reset the prefix product sequence after a zero, or equivalently, handle segments separated by zeros. If you don’t, you might end up dividing by zero or returning incorrect results. Our solution handles this by resetting the
prefixProducts
list to[1]
whenever a zero is added. -
Using the wrong condition for zero-range check: In
getProduct(k)
, we usedif k >= len(prefixProducts): return 0
. This condition works becauselen(prefixProducts)
is one more than the number of actual elements in the current segment (due to the initial 1). Another way to write this isif k > (number of elements since last zero): return 0
. Off-by-one errors here can cause mistakes. If you mistakenly used>
instead of>=
or miscomputed the comparison, you might incorrectly handle the case whenk
exactly equals the current segment length. We chose>= len(prefixProducts)
(or in Java,k >= prefix.size()
) deliberately to cover the case wherek
is exactly the length of the prefix list (meaning the range reaches into the previous segment that contained a zero). -
Edge case – all zeros: If the stream is something like
[0, 0, 0, ...]
, everyadd(0)
will reset the prefix list. AnygetProduct(k)
will find thatlen(prefixProducts) = 1
(since it resets each time), and ifk >= 1
it will return 0. This correctly handles queries on a sequence of zeros. Our approach gracefully handles this scenario: after each zero, the next query will return 0 as long as the query length is >= 1. (Also, note the problem guarantee that "current list has at least k numbers" means we wouldn’t query more elements than exist. If the stream is all zeros and you query for that many zeros, the result is 0.) -
Edge case – querying all elements: If
k
equals the total number of elements in the stream, thegetProduct(k)
should return the product of the entire stream. In the optimized approach, this will return 0 if there has been any zero in the stream at all (because the prefix segment length would be smaller than the total stream length). If there have been no zeros, it will correctly return the full product. For example, if the stream is[1,2,3,4]
and you callgetProduct(4)
, the prefix list would be[1,1,2,6,24]
and the result would be 24/1 = 24. If the stream was[1,2,0,3]
and you callgetProduct(4)
, the prefix list after the last zero covers only[3]
plus initial 1, which has length 2, sok >= len(prefixProducts)
-> returns 0, which is correct (because the entire stream of 4 includes a zero). -
Data type considerations: In languages like Java or C++, be mindful of potential overflow if you were not guaranteed by the problem that the product fits in 32 bits. In this problem, it’s guaranteed, but if it wasn’t, you might need to use a larger data type (e.g., 64-bit) or modulo arithmetic. In Python, integers can grow arbitrarily large so overflow isn’t a concern in implementation (though it could become slow if numbers were huge without the given guarantee). Always heed the problem constraint about product fitting in 32-bit, which simplifies the implementation.
-
Using division vs. multiplication: Some might try to maintain a running product of only the last k elements by dividing out the oldest element when a new one comes in (like a sliding window). This becomes tricky when zeros are involved and because
k
can change for each query (we don’t have a fixed window size – each query might ask for a differentk
). The prefix product method with reset is a cleaner way to handle it. Trying to manually drop the oldest element’s contribution when the window moves (which is how you might handle a moving window of fixed size) is not directly applicable here, since the "window" size varies per query and the stream is ever-growing rather than sliding with a fixed size.
Alternative Variations:
-
Product of last k numbers (fixed k, streaming): If the problem were framed as “always return the product of the last k numbers after each insertion” (with a fixed
k
), one could maintain a running product in a queue of size k. However, you would still need to handle zeros (which would invalidate the entire product until the zero leaves the window). The given problem is more flexible (k can vary per query), and that’s why the prefix approach is used instead of a fixed-size window approach. -
Sum of the last k numbers: A similar design problem could ask for the sum of the last k numbers. In that case, you could use a prefix sum array or a sliding window sum. In fact, LeetCode has a problem “Moving Average from Data Stream” where you are asked to find the average of the last k elements in a stream. The typical solution uses a queue of size k and a running sum. Sum is easier than product because you don’t have the zero issue (a zero just adds 0 to sum, which is harmless), and you can subtract the element that goes out of the window when the window exceeds size k. The product scenario is trickier because of multiplication and zeros, which is why resetting on zero is crucial.
-
Range product queries on an array: The concept of prefix products can be extended. For a static array (not a stream), if you need to answer many queries of the product of elements between indices i and j, you can preprocess prefix products for the array. Then the product of a range [i, j] is
prefix[j] / prefix[i-1]
(assuming no zeros, or handling zeros by segmenting the array). If updates are happening in the middle of the array, more complex data structures like segment trees or Fenwick trees (binary indexed trees) might be used. But because our problem only appends at the end, the prefix method is very efficient. -
Handling negative numbers or modulo: This problem restricted
num
to 0 <= num <= 100 (no negatives). If negatives were allowed, the logic still works – a negative number just multiplies into the prefix. The only difference is the product could be negative or positive. If the problem asked for the product modulo some number, you could take the prefix products modulo that number (though then zero handling would mean taking mod after division, which requires modular inverses if not handling zero separately – in short, it complicates things but the idea of prefix products still applies, usually one would avoid division and instead maintain inverses or do segment tree for modulo case). These variations are beyond the scope of the basic problem but are good thought exercises on how the approach adapts. -
Using data structures like Segment Tree/Fenwick Tree: These structures can handle range product queries and point updates more generally. However, for this specific problem, the prefix approach is simpler and perfectly efficient. Segment trees would be useful if, say, you needed to get the product of any arbitrary range (not just suffixes) or if you needed to update arbitrary positions (not just append at end). Since here we always add at the end and query suffixes, the prefix method is ideal.
In summary, the technique used here (prefix products with resets on zero) is a specialized application of the prefix-sum/prefix-product concept for an append-only array with queries on the suffix. This idea can be adapted or appears in various forms in other problems, especially where we want to quickly compute results of contiguous segments of a list.
Related Problems for Further Practice:
To strengthen your understanding, you can practice these related problems and concepts:
-
LeetCode 238. Product of Array Except Self: Although not a streaming problem, it uses prefix and suffix products to compute result without directly multiplying every time. It shows how prefix products can be used in array problems.
-
LeetCode 303. Range Sum Query - Immutable: This is about prefix sums. You preprocess an array so that any query for the sum of a range can be answered in O(1) time by subtracting prefix sums. (Analogy: prefix sum for addition is like prefix product for multiplication.)
-
LeetCode 304. Range Sum Query 2D - Immutable: Extension of the above to 2D grids using 2D prefix sums.
-
LeetCode 346. Moving Average from Data Stream: Design a class to get the moving average of the last k values in a stream. It’s similar in spirit but for sums/averages; you can practice maintaining a running sum and a queue of fixed size k.
-
LeetCode 1352. Product of the Last K Numbers: (The problem we just solved.) It might be worth reimplementing it in another language or revisiting after some time to ensure the concept sticks.
-
LeetCode 1172. Dinner Plate Stacks (or other design problems): Any design question where you have to maintain a data structure and perform operations like add and get queries can help you practice careful consideration of time complexity. (Dinner Plate Stacks is more complex, but it’s an example of designing around constraints.)
-
LeetCode 362. Design Hit Counter: Another design problem where you have to return the number of hits in the past 5 minutes. While the specifics are different (and involves time-based sliding windows), it’s practice for designing efficient streaming calculations.
GET YOUR FREE
Coding Questions Catalog