{x}
blog image

Moving Average from Data Stream

Solution Explanation:

This problem requires implementing a MovingAverage class that calculates the moving average of a stream of integers within a given window size. Two approaches are presented: one using an array and another using a queue.

Approach 1: Using an Array

This approach utilizes a circular array to store the last size numbers in the stream. We maintain a running sum (s) and a counter (cnt) to track the number of elements processed.

Algorithm:

  1. Initialization: The constructor initializes the array (arr), sum (s), and counter (cnt).

  2. next(val):

    • Calculates the index (idx) in the circular array to store the new value.
    • Updates the sum by adding the new value (val) and subtracting the old value at the same index (to maintain a running sum of the window).
    • Stores the new value at the calculated index in the array.
    • Increments the counter.
    • Returns the average (sum divided by the minimum of the counter and the array size, to handle cases where the counter is less than the window size).

Time Complexity: O(1) for next operation. All operations within next are constant time.

Space Complexity: O(size) to store the array.

Approach 2: Using a Queue

This approach leverages a queue (FIFO) data structure to maintain the sliding window.

Algorithm:

  1. Initialization: The constructor initializes the queue (q), sum (s), and window size (n).

  2. next(val):

    • Checks if the queue is full (size equals n). If full, removes the oldest element from the front of the queue and updates the sum accordingly.
    • Adds the new value to the rear of the queue and updates the sum.
    • Returns the average (sum divided by the queue size).

Time Complexity: O(1) for next operation (amortized). Enqueue and dequeue operations are constant time.

Space Complexity: O(size) to store the queue.

Code Examples (with explanations integrated):

The code examples in Python, Java, C++, and Go are provided above, and each one follows the same basic algorithm as described above for the chosen approach (array or queue). The comments in the code explain specific steps. Note that the queue-based approach might be slightly more efficient in some languages because queue operations are optimized.

Choosing between the array and queue approaches is largely a matter of preference and language-specific efficiencies. Both offer the same time and space complexity for this problem. If you are working in a language where queues are particularly efficient, the queue approach might be slightly favored. If you prefer a simpler, more direct approach, the array approach might be more appealing.