Moving Average from Data Stream
Easy · Arrays & Hashing · Explanation and code example
Circular Queue with Array
How it works
A circular queue (ring buffer) avoids the overhead of shifting elements when removing from the front. We use a fixed-size array where the head pointer wraps around when it reaches the end. The element being overwritten is the one leaving the window, so we subtract it from the running sum before adding the new value. This achieves constant time per operation with minimal memory overhead.
Steps
- Initialize a fixed-size array of length
size, a head pointer, a running sum, and a count of elements seen. - When
next(val)is called:- Increment the count.
- Calculate the tail position as
(head + 1) % size, which points to the oldest element that will be replaced. - Subtract the value at the tail position from the running sum and add the new value.
- Move the head pointer forward:
head = (head + 1) % size. - Store the new value at the head position.
- Return the running sum divided by the current window size (minimum of count and size).
Complexity
- Time complexity:
O(M)- Time complexity per
next()call isO(1). Therefore, the total time complexity forMcalls isO(M)
- Time complexity per
- Space complexity:
O(N)
Where
Nis the size of the moving window andMis the number of calls made tonext.
Things to watch for
Dividing by Window Size Before It Is Full
When fewer than size elements have been added, the average should be calculated using the actual count of elements, not the window size. Dividing by size when only a few elements exist produces incorrect results. For example, with size = 3 and only one element 5, the average should be 5.0, not 5/3 = 1.67.
Not Removing the Oldest Element When Window Is Full
When using a running sum approach, forgetting to subtract the element leaving the window causes the sum to grow unbounded. After receiving more than size elements, each new element should trigger removal of the oldest one from both the queue and the running sum. Failing to do this results in the average including more elements than the window size allows.
Python code example
class MovingAverage:
def __init__(self, size: int):
self.size = size
self.queue = [0] * self.size
self.head = self.window_sum = 0
# number of elements seen so far
self.count = 0
def next(self, val: int) -> float:
self.count += 1
# calculate the new sum by shifting the window
tail = (self.head + 1) % self.size
self.window_sum = self.window_sum - self.queue[tail] + val
# move on to the next head
self.head = (self.head + 1) % self.size
self.queue[self.head] = val
return self.window_sum / min(self.size, self.count)
C++ code example
class MovingAverage {
private:
int size;
int head = 0;
int windowSum = 0;
int count = 0;
vector<int> queue;
public:
MovingAverage(int size) {
this->size = size;
queue = vector<int>(size);
}
double next(int val) {
++count;
// calculate the new sum by shifting the window
int tail = (head + 1) % size;
windowSum = windowSum - queue[tail] + val;
// move on to the next head
head = (head + 1) % size;
queue[head] = val;
return windowSum * 1.0 / min(size, count);
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.