You are given two integers, m
and k
, and a stream of integers. You are tasked to implement a data structure that calculates the MKAverage for the stream.
The MKAverage can be calculated using these steps:
m
you should consider the MKAverage to be -1
. Otherwise, copy the last m
elements of the stream to a separate container.k
elements and the largest k
elements from the container.Implement the MKAverage
class:
MKAverage(int m, int k)
Initializes the MKAverage object with an empty stream and the two integers m
and k
.void addElement(int num)
Inserts a new element num
into the stream.int calculateMKAverage()
Calculates and returns the MKAverage for the current stream rounded down to the nearest integer.
Example 1:
Input
["MKAverage", "addElement", "addElement", "calculateMKAverage", "addElement", "calculateMKAverage", "addElement", "addElement", "addElement", "calculateMKAverage"]
[[3, 1], [3], [1], [], [10], [], [5], [5], [5], []]
Output
[null, null, null, -1, null, 3, null, null, null, 5]
Explanation
MKAverage obj = new MKAverage(3, 1);
obj.addElement(3); // current elements are [3]
obj.addElement(1); // current elements are [3,1]
obj.calculateMKAverage(); // return -1, because m = 3 and only 2 elements exist.
obj.addElement(10); // current elements are [3,1,10]
obj.calculateMKAverage(); // The last 3 elements are [3,1,10].
// After removing smallest and largest 1 element the container will be [3].
// The average of [3] equals 3/1 = 3, return 3
obj.addElement(5); // current elements are [3,1,10,5]
obj.addElement(5); // current elements are [3,1,10,5,5]
obj.addElement(5); // current elements are [3,1,10,5,5,5]
obj.calculateMKAverage(); // The last 3 elements are [5,5,5].
// After removing smallest and largest 1 element the container will be [5].
// The average of [5] equals 5/1 = 5, return 5
Constraints:
3 <= m <= 105
1 <= k*2 < m
1 <= num <= 105
105
calls will be made to addElement
and calculateMKAverage
.This problem requires designing a data structure that efficiently calculates the MKAverage of a stream of integers. The MKAverage is calculated by taking the last m
elements, removing the smallest and largest k
elements, and then averaging the remaining elements.
The optimal solution leverages the properties of ordered sets (or sorted containers) and queues to achieve efficient addition and calculation of the average. Let's break down the approach:
Data Structures:
Queue (q
): A deque (double-ended queue) is used to store the stream of integers. This allows for O(1) time complexity for adding elements at the end and removing elements from the beginning. The queue maintains the last m
elements.
Ordered Sets (lo
, mid
, hi
): Three ordered sets (e.g., SortedList
in Python, TreeMap
in Java, multiset
in C++, or a self-balancing binary search tree in Go) are used to efficiently manage the smallest k
elements (lo
), the middle elements (mid
), and the largest k
elements (hi
). These sets provide O(log m) time complexity for insertion, deletion, and finding min/max elements.
Sum (s
): A variable s
keeps track of the sum of elements in the mid
set. This is crucial for efficiently calculating the average.
Algorithm:
__init__(m, k)
: Initializes the data structures with m
and k
.
addElement(num)
:
num
to the queue q
.m
, remove the oldest element from the queue and update the ordered sets and s
accordingly. This is done by checking which set the removed element belongs to and adjusting the sets and sum.num
into one of the three ordered sets (lo
, mid
, or hi
) based on its value relative to the existing elements.lo
and hi
to be less than or equal to k
. If they exceed k
, transfer elements from lo
or hi
to mid
, updating s
.lo
or hi
have fewer than k
elements, transfer elements from mid
to lo
or hi
, updating s
.calculateMKAverage()
:
m
, return -1 (not enough elements).s / (m - 2 * k)
, which is the average of the remaining elements after removing the smallest and largest k
elements.Time Complexity Analysis:
addElement(num)
: O(log m) due to the operations on the ordered sets.calculateMKAverage()
: O(1) as it directly uses the pre-calculated sum s
.Space Complexity Analysis: O(m) to store the queue and the elements in the ordered sets.
Code Examples (Python): (Other languages are similar in structure)
from sortedcontainers import SortedList
from collections import deque
class MKAverage:
def __init__(self, m: int, k: int):
self.m = m
self.k = k
self.q = deque()
self.lo = SortedList()
self.mid = SortedList()
self.hi = SortedList()
self.s = 0
def addElement(self, num: int) -> None:
self.q.append(num)
if len(self.q) > self.m:
removed = self.q.popleft()
if removed in self.lo:
self.lo.remove(removed)
elif removed in self.mid:
self.mid.remove(removed)
self.s -= removed
else:
self.hi.remove(removed)
# ... (rest of addElement logic as described above) ...
def calculateMKAverage(self) -> int:
if len(self.q) < self.m:
return -1
# ... (calculation logic as described above) ...
This detailed explanation provides a comprehensive understanding of the algorithm, data structures, and complexity analysis for solving the "Finding MK Average" problem efficiently. Remember to adapt the code to your preferred programming language, using appropriate ordered set implementations.