@@ -102,7 +102,23 @@ statisticsTracker.getMode(); // return 5</div>
102
102
103
103
<!-- solution:start -->
104
104
105
- ### 方法一
105
+ ### 方法一:队列 + 哈希表 + 有序集合
106
+
107
+ 我们定义一个队列 $\textit{q}$,用来存储添加的数字,一个变量 $\textit{s}$,用来存储所有数字的和,一个哈希表 $\textit{cnt}$,用来存储每个数字的出现次数,一个有序集合 $\textit{sl}$,用来存储所有数字,一个有序集合 $\textit{sl2}$,用来存储所有数字及其出现次数,按照出现次数降序、数值升序的顺序。
108
+
109
+ 在 ` addNumber ` 方法中,我们将数字添加到队列 $\textit{q}$ 中,将数字添加到有序集合 $\textit{sl}$ 中,然后先将数字及其出现次数从有序集合 $\textit{sl2}$ 中删除,再更新数字的出现次数,最后将数字及其出现次数添加到有序集合 $\textit{sl2}$ 中,并更新所有数字的和。时间复杂度为 $O(\log n)$。
110
+
111
+ 在 ` removeFirstAddedNumber ` 方法中,我们从队列 $\textit{q}$ 中删除最早添加的数字,从有序集合 $\textit{sl}$ 中删除数字,然后先将数字及其出现次数从有序集合 $\textit{sl2}$ 中删除,再更新数字的出现次数,最后将数字及其出现次数添加到有序集合 $\textit{sl2}$ 中,并更新所有数字的和。时间复杂度为 $O(\log n)$。
112
+
113
+ 在 ` getMean ` 方法中,我们返回所有数字的和除以数字的数量,时间复杂度为 $O(1)$。
114
+
115
+ 在 ` getMedian ` 方法中,我们返回有序集合 $\textit{sl}$ 中的第 $\textit{len}(\textit{q}) / 2$ 个数字,时间复杂度为 $O(1)$ 或 $O(\log n)$。
116
+
117
+ 在 ` getMode ` 方法中,我们返回有序集合 $\textit{sl2}$ 中的第一个数字,时间复杂度 $O(1)$。
118
+
119
+ > 在 Python 中,我们可以直接按下标获取有序集合中的元素,在其它语言中,我们可以通过对顶堆实现。
120
+
121
+ 空间复杂度 $O(n)$,其中 $n$ 为添加的数字的数量。
106
122
107
123
<!-- tabs:start -->
108
124
@@ -159,13 +175,225 @@ class StatisticsTracker:
159
175
#### Java
160
176
161
177
``` java
162
-
178
+ class MedianFinder {
179
+ private final PriorityQueue<Integer > small = new PriorityQueue<> (Comparator . reverseOrder());
180
+ private final PriorityQueue<Integer > large = new PriorityQueue<> ();
181
+ private final Map<Integer , Integer > delayed = new HashMap<> ();
182
+ private int smallSize;
183
+ private int largeSize;
184
+
185
+ public void addNum (int num ) {
186
+ if (small. isEmpty() || num <= small. peek()) {
187
+ small. offer(num);
188
+ ++ smallSize;
189
+ } else {
190
+ large. offer(num);
191
+ ++ largeSize;
192
+ }
193
+ rebalance();
194
+ }
195
+
196
+ public Integer findMedian () {
197
+ return smallSize == largeSize ? large. peek() : small. peek();
198
+ }
199
+
200
+ public void removeNum (int num ) {
201
+ delayed. merge(num, 1 , Integer :: sum);
202
+ if (num <= small. peek()) {
203
+ -- smallSize;
204
+ if (num == small. peek()) {
205
+ prune(small);
206
+ }
207
+ } else {
208
+ -- largeSize;
209
+ if (num == large. peek()) {
210
+ prune(large);
211
+ }
212
+ }
213
+ rebalance();
214
+ }
215
+
216
+ private void prune (PriorityQueue<Integer > pq ) {
217
+ while (! pq. isEmpty() && delayed. containsKey(pq. peek())) {
218
+ if (delayed. merge(pq. peek(), - 1 , Integer :: sum) == 0 ) {
219
+ delayed. remove(pq. peek());
220
+ }
221
+ pq. poll();
222
+ }
223
+ }
224
+
225
+ private void rebalance () {
226
+ if (smallSize > largeSize + 1 ) {
227
+ large. offer(small. poll());
228
+ -- smallSize;
229
+ ++ largeSize;
230
+ prune(small);
231
+ } else if (smallSize < largeSize) {
232
+ small. offer(large. poll());
233
+ -- largeSize;
234
+ ++ smallSize;
235
+ prune(large);
236
+ }
237
+ }
238
+ }
239
+
240
+ class StatisticsTracker {
241
+ private final Deque<Integer > q = new ArrayDeque<> ();
242
+ private long s;
243
+ private final Map<Integer , Integer > cnt = new HashMap<> ();
244
+ private final MedianFinder medianFinder = new MedianFinder ();
245
+ private final TreeSet<int[]> ts
246
+ = new TreeSet<> ((a, b) - > a[1 ] == b[1 ] ? a[0 ] - b[0 ] : b[1 ] - a[1 ]);
247
+
248
+ public StatisticsTracker () {
249
+ }
250
+
251
+ public void addNumber (int number ) {
252
+ q. offerLast(number);
253
+ s += number;
254
+ ts. remove(new int [] {number, cnt. getOrDefault(number, 0 )});
255
+ cnt. merge(number, 1 , Integer :: sum);
256
+ medianFinder. addNum(number);
257
+ ts. add(new int [] {number, cnt. get(number)});
258
+ }
259
+
260
+ public void removeFirstAddedNumber () {
261
+ int number = q. pollFirst();
262
+ s -= number;
263
+ ts. remove(new int [] {number, cnt. get(number)});
264
+ cnt. merge(number, - 1 , Integer :: sum);
265
+ medianFinder. removeNum(number);
266
+ ts. add(new int [] {number, cnt. get(number)});
267
+ }
268
+
269
+ public int getMean () {
270
+ return (int ) (s / q. size());
271
+ }
272
+
273
+ public int getMedian () {
274
+ return medianFinder. findMedian();
275
+ }
276
+
277
+ public int getMode () {
278
+ return ts. first()[0 ];
279
+ }
280
+ }
163
281
```
164
282
165
283
#### C++
166
284
167
285
``` cpp
168
-
286
+ class MedianFinder {
287
+ public:
288
+ void addNum(int num) {
289
+ if (small.empty() || num <= small.top()) {
290
+ small.push(num);
291
+ ++smallSize;
292
+ } else {
293
+ large.push(num);
294
+ ++largeSize;
295
+ }
296
+ reblance();
297
+ }
298
+
299
+ void removeNum(int num) {
300
+ ++delayed[num];
301
+ if (num <= small.top()) {
302
+ --smallSize;
303
+ if (num == small.top()) {
304
+ prune(small);
305
+ }
306
+ } else {
307
+ --largeSize;
308
+ if (num == large.top()) {
309
+ prune (large);
310
+ }
311
+ }
312
+ reblance();
313
+ }
314
+
315
+ int findMedian() {
316
+ return smallSize == largeSize ? large.top() : small.top();
317
+ }
318
+
319
+ private:
320
+ priority_queue<int > small;
321
+ priority_queue<int, vector<int >, greater<int >> large;
322
+ unordered_map<int, int> delayed;
323
+ int smallSize = 0;
324
+ int largeSize = 0;
325
+
326
+ template <typename T>
327
+ void prune(T& pq) {
328
+ while (!pq.empty() && delayed[pq.top()]) {
329
+ if (--delayed[pq.top()] == 0) {
330
+ delayed.erase(pq.top());
331
+ }
332
+ pq.pop();
333
+ }
334
+ }
335
+
336
+ void reblance() {
337
+ if (smallSize > largeSize + 1) {
338
+ large.push(small.top());
339
+ small.pop();
340
+ --smallSize;
341
+ ++largeSize;
342
+ prune (small);
343
+ } else if (smallSize < largeSize) {
344
+ small.push(large.top());
345
+ large.pop();
346
+ ++smallSize;
347
+ --largeSize;
348
+ prune(large);
349
+ }
350
+ }
351
+ };
352
+
353
+ class StatisticsTracker {
354
+ private:
355
+ queue<int > q;
356
+ long long s = 0;
357
+ unordered_map<int, int> cnt;
358
+ MedianFinder medianFinder;
359
+ set<pair<int, int>> ts;
360
+
361
+ public:
362
+ StatisticsTracker() {}
363
+
364
+ void addNumber(int number) {
365
+ q.push(number);
366
+ s += number;
367
+ ts.erase({-cnt[number], number});
368
+ cnt[number]++;
369
+ medianFinder.addNum(number);
370
+ ts.insert({-cnt[number], number});
371
+ }
372
+
373
+ void removeFirstAddedNumber () {
374
+ int number = q.front();
375
+ q.pop();
376
+ s -= number;
377
+ ts.erase({-cnt[number], number});
378
+ cnt[number]--;
379
+ if (cnt[number] > 0) {
380
+ ts.insert({-cnt[number], number});
381
+ }
382
+ medianFinder.removeNum(number);
383
+ }
384
+
385
+ int getMean() {
386
+ return static_cast<int>(s / q.size());
387
+ }
388
+
389
+ int getMedian() {
390
+ return medianFinder.findMedian();
391
+ }
392
+
393
+ int getMode() {
394
+ return ts.begin()->second;
395
+ }
396
+ };
169
397
```
170
398
171
399
#### Go
0 commit comments