1825. Finding MK Average
Description
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:
- If the number of the elements in the stream is less than
myou should consider the MKAverage to be-1. Otherwise, copy the lastmelements of the stream to a separate container. - Remove the smallest
kelements and the largestkelements from the container. - Calculate the average value for the rest of the elements rounded down to the nearest integer.
Implement the MKAverage class:
MKAverage(int m, int k)Initializes the MKAverage object with an empty stream and the two integersmandk.void addElement(int num)Inserts a new elementnuminto 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 <= 1051 < k*2 < m1 <= num <= 105- At most
105calls will be made toaddElementandcalculateMKAverage.
Solutions
Solution 1: Ordered Set + Queue
Thinking
We need the average of an \(m\)-length window after dropping the smallest and largest \(k\) values, with frequent insertions. Sorting the window every time is \(O(m\log m)\) and too slow.
A queue stores insertion order. Three ordered multisets hold the lowest \(k\), the middle segment, and the highest \(k\), together with the middle sum \(s\). After each insert or pop we move overflow elements so both ends have size \(k\). The average is \(s/(m-2k)\).
We can maintain the following data structures or variables:
- A queue \(q\) of length \(m\), where the head of the queue is the earliest added element, and the tail of the queue is the most recently added element;
- Three ordered sets, namely \(lo\), \(mid\), \(hi\), where \(lo\) and \(hi\) store the smallest \(k\) elements and the largest \(k\) elements respectively, and \(mid\) stores the remaining elements;
- A variable \(s\), maintaining the sum of all elements in \(mid\);
- Some programming languages (such as Java, Go) additionally maintain two variables \(size1\) and \(size3\), representing the number of elements in \(lo\) and \(hi\) respectively.
When calling the \(addElement(num)\) function, perform the following operations in order:
- If \(lo\) is empty, or \(num \leq max(lo)\), then add \(num\) to \(lo\); otherwise if \(hi\) is empty, or \(num \geq min(hi)\), then add \(num\) to \(hi\); otherwise add \(num\) to \(mid\), and add the value of \(num\) to \(s\).
- Next, add \(num\) to the queue \(q\). If the length of the queue \(q\) is greater than \(m\) at this time, remove the head element \(x\) from the queue \(q\), then choose one of \(lo\), \(mid\) or \(hi\) that contains \(x\), and remove \(x\) from this set. If the set is \(mid\), subtract the value of \(x\) from \(s\).
- If the length of \(lo\) is greater than \(k\), then repeatedly remove the maximum value \(max(lo)\) from \(lo\), add \(max(lo)\) to \(mid\), and add the value of \(max(lo)\) to \(s\).
- If the length of \(hi\) is greater than \(k\), then repeatedly remove the minimum value \(min(hi)\) from \(hi\), add \(min(hi)\) to \(mid\), and add the value of \(min(hi)\) to \(s\).
- If the length of \(lo\) is less than \(k\) and \(mid\) is not empty, then repeatedly remove the minimum value \(min(mid)\) from \(mid\), add \(min(mid)\) to \(lo\), and subtract the value of \(min(mid)\) from \(s\).
- If the length of \(hi\) is less than \(k\) and \(mid\) is not empty, then repeatedly remove the maximum value \(max(mid)\) from \(mid\), add \(max(mid)\) to \(hi\), and subtract the value of \(max(mid)\) from \(s\).
When calling the \(calculateMKAverage()\) function, if the length of \(q\) is less than \(m\), return \(-1\), otherwise return \(\frac{s}{m - 2k}\).
In terms of time complexity, each call to the \(addElement(num)\) function has a time complexity of \(O(\log m)\), and each call to the \(calculateMKAverage()\) function has a time complexity of \(O(1)\). The space complexity is \(O(m)\).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | |
Solution 2: Single Ordered Set + Sliding Window
Thinking
Solution 1 juggles three trees and their size invariants. A single ordered list of the window is enough: build it when the window first fills, then adjust \(s\) by the rank of the inserted and deleted values. The structure is simpler and each update remains logarithmic.
Use a queue for insertion order and one ordered set for the current window of length \(m\). When the window first becomes full, build the set and sum the middle segment after dropping the smallest and largest \(k\) elements. Later insertions and deletions update that middle sum from the element's rank in the set.
Unlike method 1, which partitions the window into three sets, this approach keeps a single ordered sequence.
Each addElement call takes \(O(\log m)\) time, and each calculateMKAverage call takes \(O(1)\) time. The space complexity is \(O(m)\).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |