1352. 最后 K 个数的乘积
1352. 最后 K 个数的乘积
🟠 🔖 设计
队列
数组
数学
数据流
🔗 力扣
LeetCode
题目
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 integernum
to the stream.int getProduct(int k)
Returns the product of the lastk
numbers in the current list. You can assume that always the current list has at leastk
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 * 10^4
- At most
4 * 10^4
calls will be made toadd
andgetProduct
. - The product of the stream at any point in time will fit in a 32-bit integer.
题目大意
请你实现一个「数字乘积类」ProductOfNumbers
,要求支持下述两种方法:
add(int num)
- 将数字
num
添加到当前数字列表的最后面。
getProduct(int k)
- 返回当前数字列表中,最后
k
个数字的乘积。 - 你可以假设当前列表中始终 至少 包含
k
个数字。
题目数据保证:任何时候,任一连续数字序列的乘积都在 32-bit 整数范围内,不会溢出。
示例:
输入:
["ProductOfNumbers","add","add","add","add","add","getProduct","getProduct","getProduct","add","getProduct"]
[[],[3],[0],[2],[5],[4],[2],[3],[4],[8],[2]]
输出:
[null,null,null,null,null,null,20,40,0,null,32]
解释:
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); // 返回 20 。最后 2 个数字的乘积是 5 \* 4 = 20 productOfNumbers.getProduct(3); // 返回 40 。最后 3 个数字的乘积是 2 \* 5 \* 4 = 40 productOfNumbers.getProduct(4); // 返回 0 。最后 4 个数字的乘积是 0 \* 2 \* 5 \* 4 = 0 productOfNumbers.add(8); // [3,0,2,5,4,8] productOfNumbers.getProduct(2); // 返回 32 。最后 2 个数字的乘积是 4 \* 8 = 32
提示:
add
和getProduct
两种操作加起来总共不会超过40000
次。0 <= num <= 100
1 <= k <= 40000
解题思路
使用前缀乘积(Prefix Product)优化计算:
- 维护一个
arr
数组,其中arr[i]
存储的是前i
个数字的累积乘积(prefix product)。 - 这样,在查询最后
k
个数的乘积时,可以直接用 除法 计算:getProduct(k) = arr[size]/arr[size - k]
这一技巧可以 O(1) 计算乘积,避免暴力遍历计算,提升查询效率。
- 维护一个
处理
0
的情况:- 由于除法不适用于 0,需要特殊处理。当遇到
num == 0
时,数组arr
需要重置为[1]
,并将size
设为0
,相当于清空之前的记录。 - 这是因为
0
会破坏前缀乘积的连贯性(0 乘任何数都为 0),因此之前的所有计算结果都不再有效。
- 由于除法不适用于 0,需要特殊处理。当遇到
复杂度分析
add(num)
操作:- 时间复杂度:
O(1)
,因为只需要在arr
末尾追加一个元素。 - 空间复杂度:
O(n)
,n
是调用add()
的次数。
- 时间复杂度:
getProduct(k)
操作:- 时间复杂度:
O(1)
,因为计算乘积只需一次除法运算。 - 空间复杂度:
O(1)
,因为不会使用额外的存储空间。
- 时间复杂度:
代码
var ProductOfNumbers = function () {
this.arr = [1];
this.size = 0;
};
/**
* @param {number} num
* @return {void}
*/
ProductOfNumbers.prototype.add = function (num) {
if (num == 0) {
this.arr = [1];
this.size = 0;
} else {
this.arr.push(this.arr[this.size] * num);
this.size++;
}
};
/**
* @param {number} k
* @return {number}
*/
ProductOfNumbers.prototype.getProduct = function (k) {
if (k > this.size) return 0;
return this.arr[this.size] / this.arr[this.size - k];
};