Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.
Implement the MovingAverage
class:
MovingAverage(int size)
Initializes the object with the size of the window size
.double next(int val)
Returns the moving average of the last size
values of the stream.Example 1:
Input ["MovingAverage", "next", "next", "next", "next"] [[3], [1], [10], [3], [5]] Output [null, 1.0, 5.5, 4.66667, 6.0] Explanation MovingAverage movingAverage = new MovingAverage(3); movingAverage.next(1); // return 1.0 = 1 / 1 movingAverage.next(10); // return 5.5 = (1 + 10) / 2 movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3 movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3
Constraints:
1 <= size <= 1000
-105 <= val <= 105
104
calls will be made to next
.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:
We need to calculate the average of a series of numbers as they arrive, but only for a specific number of recent entries. The brute force method keeps track of all the numbers seen so far and recalculates the average for the required recent values every time a new number comes in.
Here's how the algorithm would work step-by-step:
class MovingAverage:
def __init__(self, size):
self.size = size
self.all_numbers = []
def next(self, value):
self.all_numbers.append(value)
# Ensure we only consider the 'size' most recent numbers
window_size = min(self.size, len(self.all_numbers))
# Calculate the sum of the recent numbers
recent_numbers_sum = sum(self.all_numbers[-window_size:])
# Calculate the average
average = recent_numbers_sum / window_size
return average
The most efficient way to calculate the moving average is to keep a running sum of the numbers. Instead of recalculating the sum for each new average, we only update it by adding the new number and subtracting the oldest number.
Here's how the algorithm would work step-by-step:
class MovingAverage:
def __init__(self, size: int):
self.window_size = size
self.numbers = []
self.current_sum = 0
def next(self, value: int) -> float:
self.numbers.append(value)
self.current_sum += value
# If we exceed the window size, remove the oldest value.
if len(self.numbers) > self.window_size:
self.current_sum -= self.numbers.pop(0)
# Take care of edge cases when calculating average.
current_length = min(len(self.numbers), self.window_size)
return self.current_sum / current_length
# Your MovingAverage object will be instantiated and called as such:
# movingAverage = MovingAverage(size)
# answer = movingAverage.next(value)
Case | How to Handle |
---|---|
Window size is zero or negative | Throw an IllegalArgumentException, as a non-positive window size is invalid and doesn't make sense for a moving average. |
Input stream is empty | Return 0.0 as the moving average when the stream is empty, as there are no data points to average. |
Window size is larger than the number of elements added to the stream | Calculate the average using only the elements currently in the stream, effectively using a smaller window. |
Integers in the stream are very large, potentially leading to integer overflow during summation | Use long or double to store the sum to prevent potential integer overflows. |
Adding a very large number of elements such that the data structure storing the elements becomes very large, potentially leading to memory issues | Circular queue implementation allows for constant memory usage regardless of number of calls to next(). |
Input contains a mix of very large positive and negative values which when summed will result in loss of precision when using floats | Using double rather than float can mitigate precision loss problems. |
Repeated calls to next() with identical values exceeding maximum integer sizes can lead to overflow. | Use long or double to store intermediate calculations to prevent overflow, even if input values are integers. |
Very small window size (e.g., 1) and rapidly changing input values | The moving average will closely reflect the latest value, which is the correct behavior. |