Skip to content

Commit 94e5644

Browse files
authored
feat: add solutions to lc problems: No.3369~3373 (#3835)
1 parent e086812 commit 94e5644

File tree

12 files changed

+744
-14
lines changed

12 files changed

+744
-14
lines changed

solution/3300-3399/3369.Design an Array Statistics Tracker/README.md

+231-3
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,23 @@ statisticsTracker.getMode(); // return 5</div>
102102

103103
<!-- solution:start -->
104104

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$ 为添加的数字的数量。
106122

107123
<!-- tabs:start -->
108124

@@ -159,13 +175,225 @@ class StatisticsTracker:
159175
#### Java
160176

161177
```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+
}
163281
```
164282

165283
#### C++
166284

167285
```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+
};
169397
```
170398

171399
#### Go

0 commit comments

Comments
 (0)