Design an algorithm that accepts a stream of integers and retrieves the product of the last k
integers of the stream.
Implement the ProductOfNumbers
class:
ProductOfNumbers()
Initializes the object with an empty stream.void add(int num)
Appends the integer num
to the stream.int getProduct(int k)
Returns the product of the last k
numbers in the current list. You can assume that always the current list has at least k
numbers.The test cases are generated so that, at any time, the product of any contiguous sequence of numbers will fit into a single 32-bit integer without overflowing.
Example:
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(); productOfNumbers.add(3); // [3] productOfNumbers.add(0); // [3,0] productOfNumbers.add(2); // [3,0,2] productOfNumbers.add(5); // [3,0,2,5] productOfNumbers.add(4); // [3,0,2,5,4] productOfNumbers.getProduct(2); // return 20. The product of the last 2 numbers is 5 * 4 = 20 productOfNumbers.getProduct(3); // return 40. The product of the last 3 numbers is 2 * 5 * 4 = 40 productOfNumbers.getProduct(4); // return 0. The product of the last 4 numbers is 0 * 2 * 5 * 4 = 0 productOfNumbers.add(8); // [3,0,2,5,4,8] productOfNumbers.getProduct(2); // return 32. The product of the last 2 numbers is 4 * 8 = 32
Constraints:
0 <= num <= 100
1 <= k <= 4 * 104
4 * 104
calls will be made to add
and getProduct
.GetProduct
and Add
to work in O(1)
time complexity instead of O(k)
time complexity?When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The brute force method for this problem means we're going to calculate the product every time we need it, without saving any previous results. We will keep a running record of all the numbers we've seen, and compute the result using only that record.
Here's how the algorithm would work step-by-step:
class ProductOfNumbers:
def __init__(self):
self.numbers_list = []
def add(self, number: int) -> None:
# Append the new number to the list of numbers.
self.numbers_list.append(number)
def getProduct(self, k: int) -> int:
# Extract the last 'k' numbers from the list.
last_k_numbers = self.numbers_list[-k:]
product_of_last_k_numbers = 1
# Calculate the product of the last 'k' numbers.
for number in last_k_numbers:
product_of_last_k_numbers *= number
return product_of_last_k_numbers
Instead of recalculating the product every time, the smart approach is to keep a running record of products. This way, you can quickly find the product of the last 'k' numbers by using division with the precomputed products.
Here's how the algorithm would work step-by-step:
class ProductOfNumbers:
def __init__(self):
self.cumulative_products = [1]
def add(self, number):
if number == 0:
# Reset list on zero because all future products are zero.
self.cumulative_products = [1]
else:
self.cumulative_products.append(self.cumulative_products[-1] * number)
def getProduct(self, k_numbers):
if k_numbers >= len(self.cumulative_products):
# Not enough numbers to form product
return 0
divisor_index = len(self.cumulative_products) - k_numbers - 1
# Avoid division by zero if a zero exists in the last K numbers
if self.cumulative_products[divisor_index] == 0:
return 0
return self.cumulative_products[-1] // self.cumulative_products[divisor_index]
Case | How to Handle |
---|---|
Adding zero to the stream. | Reset the product array because any product involving zero becomes zero. |
Requesting product of more numbers than added so far (k > size of stream). | Return 0 as no valid product can be computed given the constraint. |
Large number of add operations, potentially exceeding memory limits. | The solution needs to be space optimized, for example only keep prefix products, or use an alternative data structure. |
Large input numbers potentially leading to integer overflow in the product calculation. | Use a larger data type such as long or handle the overflow and return a specific error value. |
Multiple consecutive zeros are added to the stream. | The product array needs to be reset for each zero encountered. |
k is zero. | Return 1 as the product of no numbers is typically defined as 1. |
Negative numbers in the stream. | The product array should handle negative numbers correctly as a negative times a negative is a positive. |
Very large k, approaching the maximum integer value. | Ensure the solution doesn't run into integer overflow issues when handling such a large k value and that the implementation scales to such large values. |