from collections import deque
class MovingAverage:
def __init__(self, size: int):
self.size = size
self.window = deque()
self.window_sum = 0
def next(self, val: int) -> float:
self.window.append(val)
self.window_sum += val
if len(self.window) > self.size:
removed = self.window.popleft()
self.window_sum -= removed
return self.window_sum / len(self.window)
#include <queue>
class MovingAverage {
private:
std::queue<int> window;
int maxSize;
double windowSum;
public:
MovingAverage(int size) : maxSize(size), windowSum(0) {}
double next(int val) {
window.push(val);
windowSum += val;
if (window.size() > maxSize) {
windowSum -= window.front();
window.pop();
}
return windowSum / window.size();
}
};
import java.util.*;
class MovingAverage {
private Queue<Integer> window;
private int maxSize;
private double windowSum;
public MovingAverage(int size) {
this.maxSize = size;
this.window = new LinkedList<>();
this.windowSum = 0;
}
public double next(int val) {
window.add(val);
windowSum += val;
if (window.size() > maxSize) {
windowSum -= window.poll();
}
return windowSum / window.size();
}
}
class MovingAverage {
constructor(size) {
this.size = size;
this.queue = [];
this.windowSum = 0;
}
next(val) {
this.queue.push(val);
this.windowSum += val;
if (this.queue.length > this.size) {
this.windowSum -= this.queue.shift();
}
return this.windowSum / this.queue.length;
}
}
The "Moving Average from Data Stream" problem asks you to design a class that calculates the moving average of the last size
integers in a stream of numbers.
Specifically, you will implement a class with:
size
and sets the window size for the moving average.next(val)
that accepts an integer val
, adds it to the stream, and returns the average of the last size
elements (or all elements so far if fewer than size
).next
adds a new integer to the stream.size
elements.
When approaching this problem, the first idea is to keep track of all numbers seen so far, and every time next
is called, compute the average of the last size
elements. This could be done by storing all numbers in a list or array and, for each call, slicing the last size
elements to compute the sum and average.
However, this brute-force approach is inefficient because each call would require summing up to size
numbers, leading to unnecessary repeated work, especially as the stream grows. To optimize, we need a way to:
size
To solve the problem efficiently, we use a sliding window of size size
to keep track of the most recent numbers. Here’s the step-by-step strategy:
size
).next(val)
is called, add val
to the end of the queue and increase the running sum by val
.size
, remove the oldest element from the front and decrease the running sum by that value.running sum / current window size
.Using a queue ensures that both adding and removing elements are efficient (O(1) time), and maintaining a running sum means the average can be computed quickly without iterating over the window each time.
Let's walk through an example with size = 3
and a stream of values: [1, 10, 3, 5]
.
next(1)
:
next(10)
:
next(3)
:
next(5)
:
size
elements, efficiently updating the sum and average.
Brute-force Approach:
next
call, because we would sum up to size
elements every time.next
call. Both adding/removing from the queue and updating the sum are constant time.size
elements in the window at any time.The optimized approach is much more scalable, especially for large streams and small window sizes.
In summary, the "Moving Average from Data Stream" problem is elegantly solved using a sliding window with a queue (or deque) and a running sum. This approach allows us to efficiently maintain the average of the most recent size
elements in constant time and space relative to the window size. The key insight is to only store what's necessary and to update the sum incrementally, avoiding unnecessary recomputation.