Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add solutions to lc problems: No.3369~3373 #3835

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 231 additions & 3 deletions solution/3300-3399/3369.Design an Array Statistics Tracker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,23 @@ statisticsTracker.getMode(); // return 5</div>

<!-- solution:start -->

### 方法一
### 方法一:队列 + 哈希表 + 有序集合

我们定义一个队列 $\textit{q}$,用来存储添加的数字,一个变量 $\textit{s}$,用来存储所有数字的和,一个哈希表 $\textit{cnt}$,用来存储每个数字的出现次数,一个有序集合 $\textit{sl}$,用来存储所有数字,一个有序集合 $\textit{sl2}$,用来存储所有数字及其出现次数,按照出现次数降序、数值升序的顺序。

在 `addNumber` 方法中,我们将数字添加到队列 $\textit{q}$ 中,将数字添加到有序集合 $\textit{sl}$ 中,然后先将数字及其出现次数从有序集合 $\textit{sl2}$ 中删除,再更新数字的出现次数,最后将数字及其出现次数添加到有序集合 $\textit{sl2}$ 中,并更新所有数字的和。时间复杂度为 $O(\log n)$。

在 `removeFirstAddedNumber` 方法中,我们从队列 $\textit{q}$ 中删除最早添加的数字,从有序集合 $\textit{sl}$ 中删除数字,然后先将数字及其出现次数从有序集合 $\textit{sl2}$ 中删除,再更新数字的出现次数,最后将数字及其出现次数添加到有序集合 $\textit{sl2}$ 中,并更新所有数字的和。时间复杂度为 $O(\log n)$。

在 `getMean` 方法中,我们返回所有数字的和除以数字的数量,时间复杂度为 $O(1)$。

在 `getMedian` 方法中,我们返回有序集合 $\textit{sl}$ 中的第 $\textit{len}(\textit{q}) / 2$ 个数字,时间复杂度为 $O(1)$ 或 $O(\log n)$。

在 `getMode` 方法中,我们返回有序集合 $\textit{sl2}$ 中的第一个数字,时间复杂度 $O(1)$。

> 在 Python 中,我们可以直接按下标获取有序集合中的元素,在其它语言中,我们可以通过对顶堆实现。

空间复杂度 $O(n)$,其中 $n$ 为添加的数字的数量。

<!-- tabs:start -->

Expand Down Expand Up @@ -159,13 +175,225 @@ class StatisticsTracker:
#### Java

```java

class MedianFinder {
private final PriorityQueue<Integer> small = new PriorityQueue<>(Comparator.reverseOrder());
private final PriorityQueue<Integer> large = new PriorityQueue<>();
private final Map<Integer, Integer> delayed = new HashMap<>();
private int smallSize;
private int largeSize;

public void addNum(int num) {
if (small.isEmpty() || num <= small.peek()) {
small.offer(num);
++smallSize;
} else {
large.offer(num);
++largeSize;
}
rebalance();
}

public Integer findMedian() {
return smallSize == largeSize ? large.peek() : small.peek();
}

public void removeNum(int num) {
delayed.merge(num, 1, Integer::sum);
if (num <= small.peek()) {
--smallSize;
if (num == small.peek()) {
prune(small);
}
} else {
--largeSize;
if (num == large.peek()) {
prune(large);
}
}
rebalance();
}

private void prune(PriorityQueue<Integer> pq) {
while (!pq.isEmpty() && delayed.containsKey(pq.peek())) {
if (delayed.merge(pq.peek(), -1, Integer::sum) == 0) {
delayed.remove(pq.peek());
}
pq.poll();
}
}

private void rebalance() {
if (smallSize > largeSize + 1) {
large.offer(small.poll());
--smallSize;
++largeSize;
prune(small);
} else if (smallSize < largeSize) {
small.offer(large.poll());
--largeSize;
++smallSize;
prune(large);
}
}
}

class StatisticsTracker {
private final Deque<Integer> q = new ArrayDeque<>();
private long s;
private final Map<Integer, Integer> cnt = new HashMap<>();
private final MedianFinder medianFinder = new MedianFinder();
private final TreeSet<int[]> ts
= new TreeSet<>((a, b) -> a[1] == b[1] ? a[0] - b[0] : b[1] - a[1]);

public StatisticsTracker() {
}

public void addNumber(int number) {
q.offerLast(number);
s += number;
ts.remove(new int[] {number, cnt.getOrDefault(number, 0)});
cnt.merge(number, 1, Integer::sum);
medianFinder.addNum(number);
ts.add(new int[] {number, cnt.get(number)});
}

public void removeFirstAddedNumber() {
int number = q.pollFirst();
s -= number;
ts.remove(new int[] {number, cnt.get(number)});
cnt.merge(number, -1, Integer::sum);
medianFinder.removeNum(number);
ts.add(new int[] {number, cnt.get(number)});
}

public int getMean() {
return (int) (s / q.size());
}

public int getMedian() {
return medianFinder.findMedian();
}

public int getMode() {
return ts.first()[0];
}
}
```

#### C++

```cpp

class MedianFinder {
public:
void addNum(int num) {
if (small.empty() || num <= small.top()) {
small.push(num);
++smallSize;
} else {
large.push(num);
++largeSize;
}
reblance();
}

void removeNum(int num) {
++delayed[num];
if (num <= small.top()) {
--smallSize;
if (num == small.top()) {
prune(small);
}
} else {
--largeSize;
if (num == large.top()) {
prune(large);
}
}
reblance();
}

int findMedian() {
return smallSize == largeSize ? large.top() : small.top();
}

private:
priority_queue<int> small;
priority_queue<int, vector<int>, greater<int>> large;
unordered_map<int, int> delayed;
int smallSize = 0;
int largeSize = 0;

template <typename T>
void prune(T& pq) {
while (!pq.empty() && delayed[pq.top()]) {
if (--delayed[pq.top()] == 0) {
delayed.erase(pq.top());
}
pq.pop();
}
}

void reblance() {
if (smallSize > largeSize + 1) {
large.push(small.top());
small.pop();
--smallSize;
++largeSize;
prune(small);
} else if (smallSize < largeSize) {
small.push(large.top());
large.pop();
++smallSize;
--largeSize;
prune(large);
}
}
};

class StatisticsTracker {
private:
queue<int> q;
long long s = 0;
unordered_map<int, int> cnt;
MedianFinder medianFinder;
set<pair<int, int>> ts;

public:
StatisticsTracker() {}

void addNumber(int number) {
q.push(number);
s += number;
ts.erase({-cnt[number], number});
cnt[number]++;
medianFinder.addNum(number);
ts.insert({-cnt[number], number});
}

void removeFirstAddedNumber() {
int number = q.front();
q.pop();
s -= number;
ts.erase({-cnt[number], number});
cnt[number]--;
if (cnt[number] > 0) {
ts.insert({-cnt[number], number});
}
medianFinder.removeNum(number);
}

int getMean() {
return static_cast<int>(s / q.size());
}

int getMedian() {
return medianFinder.findMedian();
}

int getMode() {
return ts.begin()->second;
}
};
```

#### Go
Expand Down
Loading
Loading