Skip to content

Commit 36d3a40

Browse files
authored
feat: add solutions to lcci/lcof problems (#3170)
lcci 17.20 & lcof 41. Continuous Median
1 parent 92af611 commit 36d3a40

22 files changed

+876
-645
lines changed

lcci/17.20.Continuous Median/README.md

Lines changed: 208 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -46,41 +46,36 @@ findMedian() -> 2
4646

4747
<!-- solution:start -->
4848

49-
### 方法一:优先队列(双堆
49+
### 方法一:大小根堆(优先队列
5050

51-
创建大根堆、小根堆,其中:大根堆存放较小的一半元素,小根堆存放较大的一半元素
51+
我们可以使用两个堆来维护所有的元素,一个小根堆 $\text{minQ}$ 和一个大根堆 $\text{maxQ}$,其中小根堆 $\text{minQ}$ 存储较大的一半,大根堆 $\text{maxQ}$ 存储较小的一半
5252

53-
添加元素时,先放入小根堆,然后将小根堆对顶元素弹出并放入大根堆(使得大根堆个数多 $1$);若大小根堆元素个数差超过 $1$,则将大根堆元素弹出放入小根堆
53+
调用 `addNum` 方法时,我们首先将元素加入到大根堆 $\text{maxQ}$,然后将 $\text{maxQ}$ 的堆顶元素弹出并加入到小根堆 $\text{minQ}$。如果此时 $\text{minQ}$ 的大小与 $\text{maxQ}$ 的大小差值大于 $1$,我们就将 $\text{minQ}$ 的堆顶元素弹出并加入到 $\text{maxQ}$。时间复杂度为 $O(\log n)$
5454

55-
取中位数时,若大根堆元素较多,取大根堆堆顶,否则取两堆顶元素和的平均值
55+
调用 `findMedian` 方法时,如果 $\text{minQ}$ 的大小等于 $\text{maxQ}$ 的大小,说明元素的总数为偶数,我们就可以返回 $\text{minQ}$ 的堆顶元素与 $\text{maxQ}$ 的堆顶元素的平均值;否则,我们返回 $\text{minQ}$ 的堆顶元素。时间复杂度为 $O(1)$
5656

57-
**时间复杂度分析:**
58-
59-
每次添加元素的时间复杂度为 $O(\log n)$,取中位数的时间复杂度为 $O(1)$。
57+
空间复杂度为 $O(n)$。其中 $n$ 为元素的个数。
6058

6159
<!-- tabs:start -->
6260

6361
#### Python3
6462

6563
```python
6664
class MedianFinder:
65+
6766
def __init__(self):
68-
"""
69-
initialize your data structure here.
70-
"""
71-
self.h1 = []
72-
self.h2 = []
67+
self.minq = []
68+
self.maxq = []
7369

7470
def addNum(self, num: int) -> None:
75-
heappush(self.h1, num)
76-
heappush(self.h2, -heappop(self.h1))
77-
if len(self.h2) - len(self.h1) > 1:
78-
heappush(self.h1, -heappop(self.h2))
71+
heappush(self.minq, -heappushpop(self.maxq, -num))
72+
if len(self.minq) - len(self.maxq) > 1:
73+
heappush(self.maxq, -heappop(self.minq))
7974

8075
def findMedian(self) -> float:
81-
if len(self.h2) > len(self.h1):
82-
return -self.h2[0]
83-
return (self.h1[0] - self.h2[0]) / 2
76+
if len(self.minq) == len(self.maxq):
77+
return (self.minq[0] - self.maxq[0]) / 2
78+
return self.minq[0]
8479

8580

8681
# Your MedianFinder object will be instantiated and called as such:
@@ -93,26 +88,22 @@ class MedianFinder:
9388

9489
```java
9590
class MedianFinder {
96-
private PriorityQueue<Integer> q1 = new PriorityQueue<>();
97-
private PriorityQueue<Integer> q2 = new PriorityQueue<>(Collections.reverseOrder());
91+
private PriorityQueue<Integer> minQ = new PriorityQueue<>();
92+
private PriorityQueue<Integer> maxQ = new PriorityQueue<>(Collections.reverseOrder());
9893

99-
/** initialize your data structure here. */
10094
public MedianFinder() {
10195
}
10296

10397
public void addNum(int num) {
104-
q1.offer(num);
105-
q2.offer(q1.poll());
106-
if (q2.size() - q1.size() > 1) {
107-
q1.offer(q2.poll());
98+
maxQ.offer(num);
99+
minQ.offer(maxQ.poll());
100+
if (minQ.size() - maxQ.size() > 1) {
101+
maxQ.offer(minQ.poll());
108102
}
109103
}
110104

111105
public double findMedian() {
112-
if (q2.size() > q1.size()) {
113-
return q2.peek();
114-
}
115-
return (q1.peek() + q2.peek()) * 1.0 / 2;
106+
return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek();
116107
}
117108
}
118109

@@ -129,30 +120,27 @@ class MedianFinder {
129120
```cpp
130121
class MedianFinder {
131122
public:
132-
/** initialize your data structure here. */
133123
MedianFinder() {
134124
}
135125

136126
void addNum(int num) {
137-
q1.push(num);
138-
q2.push(q1.top());
139-
q1.pop();
140-
if (q2.size() - q1.size() > 1) {
141-
q1.push(q2.top());
142-
q2.pop();
127+
maxQ.push(num);
128+
minQ.push(maxQ.top());
129+
maxQ.pop();
130+
131+
if (minQ.size() > maxQ.size() + 1) {
132+
maxQ.push(minQ.top());
133+
minQ.pop();
143134
}
144135
}
145136

146137
double findMedian() {
147-
if (q2.size() > q1.size()) {
148-
return q2.top();
149-
}
150-
return (double) (q1.top() + q2.top()) / 2;
138+
return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top();
151139
}
152140

153141
private:
154-
priority_queue<int, vector<int>, greater<int>> q1;
155-
priority_queue<int> q2;
142+
priority_queue<int> maxQ;
143+
priority_queue<int, vector<int>, greater<int>> minQ;
156144
};
157145

158146
/**
@@ -167,37 +155,31 @@ private:
167155

168156
```go
169157
type MedianFinder struct {
170-
q1 hp
171-
q2 hp
158+
minq hp
159+
maxq hp
172160
}
173161

174-
/** initialize your data structure here. */
175162
func Constructor() MedianFinder {
176163
return MedianFinder{hp{}, hp{}}
177164
}
178165

179166
func (this *MedianFinder) AddNum(num int) {
180-
heap.Push(&this.q1, num)
181-
heap.Push(&this.q2, -heap.Pop(&this.q1).(int))
182-
if this.q2.Len()-this.q1.Len() > 1 {
183-
heap.Push(&this.q1, -heap.Pop(&this.q2).(int))
167+
minq, maxq := &this.minq, &this.maxq
168+
heap.Push(maxq, -num)
169+
heap.Push(minq, -heap.Pop(maxq).(int))
170+
if minq.Len()-maxq.Len() > 1 {
171+
heap.Push(maxq, -heap.Pop(minq).(int))
184172
}
185173
}
186174

187175
func (this *MedianFinder) FindMedian() float64 {
188-
if this.q2.Len() > this.q1.Len() {
189-
return -float64(this.q2.IntSlice[0])
176+
minq, maxq := this.minq, this.maxq
177+
if minq.Len() == maxq.Len() {
178+
return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2
190179
}
191-
return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0
180+
return float64(minq.IntSlice[0])
192181
}
193182

194-
/**
195-
* Your MedianFinder object will be instantiated and called as such:
196-
* obj := Constructor();
197-
* obj.AddNum(num);
198-
* param_2 := obj.FindMedian();
199-
*/
200-
201183
type hp struct{ sort.IntSlice }
202184

203185
func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] }
@@ -208,32 +190,181 @@ func (h *hp) Pop() any {
208190
h.IntSlice = a[:len(a)-1]
209191
return v
210192
}
193+
194+
/**
195+
* Your MedianFinder object will be instantiated and called as such:
196+
* obj := Constructor();
197+
* obj.AddNum(num);
198+
* param_2 := obj.FindMedian();
199+
*/
200+
```
201+
202+
#### TypeScript
203+
204+
```ts
205+
class MedianFinder {
206+
#minQ = new MinPriorityQueue();
207+
#maxQ = new MaxPriorityQueue();
208+
209+
addNum(num: number): void {
210+
const [minQ, maxQ] = [this.#minQ, this.#maxQ];
211+
maxQ.enqueue(num);
212+
minQ.enqueue(maxQ.dequeue().element);
213+
if (minQ.size() - maxQ.size() > 1) {
214+
maxQ.enqueue(minQ.dequeue().element);
215+
}
216+
}
217+
218+
findMedian(): number {
219+
const [minQ, maxQ] = [this.#minQ, this.#maxQ];
220+
if (minQ.size() === maxQ.size()) {
221+
return (minQ.front().element + maxQ.front().element) / 2;
222+
}
223+
return minQ.front().element;
224+
}
225+
}
226+
227+
/**
228+
* Your MedianFinder object will be instantiated and called as such:
229+
* var obj = new MedianFinder()
230+
* obj.addNum(num)
231+
* var param_2 = obj.findMedian()
232+
*/
233+
```
234+
235+
#### Rust
236+
237+
```rust
238+
use std::cmp::Reverse;
239+
use std::collections::BinaryHeap;
240+
241+
struct MedianFinder {
242+
minQ: BinaryHeap<Reverse<i32>>,
243+
maxQ: BinaryHeap<i32>,
244+
}
245+
246+
impl MedianFinder {
247+
fn new() -> Self {
248+
MedianFinder {
249+
minQ: BinaryHeap::new(),
250+
maxQ: BinaryHeap::new(),
251+
}
252+
}
253+
254+
fn add_num(&mut self, num: i32) {
255+
self.maxQ.push(num);
256+
self.minQ.push(Reverse(self.maxQ.pop().unwrap()));
257+
258+
if self.minQ.len() > self.maxQ.len() + 1 {
259+
self.maxQ.push(self.minQ.pop().unwrap().0);
260+
}
261+
}
262+
263+
fn find_median(&self) -> f64 {
264+
if self.minQ.len() == self.maxQ.len() {
265+
let min_top = self.minQ.peek().unwrap().0;
266+
let max_top = *self.maxQ.peek().unwrap();
267+
(min_top + max_top) as f64 / 2.0
268+
} else {
269+
self.minQ.peek().unwrap().0 as f64
270+
}
271+
}
272+
}
273+
```
274+
275+
#### JavaScript
276+
277+
```js
278+
var MedianFinder = function () {
279+
this.minQ = new MinPriorityQueue();
280+
this.maxQ = new MaxPriorityQueue();
281+
};
282+
283+
/**
284+
* @param {number} num
285+
* @return {void}
286+
*/
287+
MedianFinder.prototype.addNum = function (num) {
288+
this.maxQ.enqueue(num);
289+
this.minQ.enqueue(this.maxQ.dequeue().element);
290+
if (this.minQ.size() - this.maxQ.size() > 1) {
291+
this.maxQ.enqueue(this.minQ.dequeue().element);
292+
}
293+
};
294+
295+
/**
296+
* @return {number}
297+
*/
298+
MedianFinder.prototype.findMedian = function () {
299+
if (this.minQ.size() === this.maxQ.size()) {
300+
return (this.minQ.front().element + this.maxQ.front().element) / 2;
301+
}
302+
return this.minQ.front().element;
303+
};
304+
305+
/**
306+
* Your MedianFinder object will be instantiated and called as such:
307+
* var obj = new MedianFinder()
308+
* obj.addNum(num)
309+
* var param_2 = obj.findMedian()
310+
*/
311+
```
312+
313+
#### C#
314+
315+
```cs
316+
public class MedianFinder {
317+
private PriorityQueue<int, int> minQ = new PriorityQueue<int, int>();
318+
private PriorityQueue<int, int> maxQ = new PriorityQueue<int, int>(Comparer<int>.Create((a, b) => b.CompareTo(a)));
319+
320+
public MedianFinder() {
321+
322+
}
323+
324+
public void AddNum(int num) {
325+
maxQ.Enqueue(num, num);
326+
minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue());
327+
if (minQ.Count > maxQ.Count + 1) {
328+
maxQ.Enqueue(minQ.Peek(), minQ.Dequeue());
329+
}
330+
}
331+
332+
public double FindMedian() {
333+
return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek();
334+
}
335+
}
336+
337+
/**
338+
* Your MedianFinder object will be instantiated and called as such:
339+
* MedianFinder obj = new MedianFinder();
340+
* obj.AddNum(num);
341+
* double param_2 = obj.FindMedian();
342+
*/
211343
```
212344

213345
#### Swift
214346

215347
```swift
216348
class MedianFinder {
217-
private var minHeap = Heap<Int>(sort: <)
218-
private var maxHeap = Heap<Int>(sort: >)
349+
private var minQ = Heap<Int>(sort: <)
350+
private var maxQ = Heap<Int>(sort: >)
219351

220352
init() {
221353
}
222354

223355
func addNum(_ num: Int) {
224-
maxHeap.insert(num)
225-
minHeap.insert(maxHeap.remove()!)
226-
227-
if maxHeap.count < minHeap.count {
228-
maxHeap.insert(minHeap.remove()!)
356+
maxQ.insert(num)
357+
minQ.insert(maxQ.remove()!)
358+
if maxQ.count < minQ.count {
359+
maxQ.insert(minQ.remove()!)
229360
}
230361
}
231362

232363
func findMedian() -> Double {
233-
if maxHeap.count > minHeap.count {
234-
return Double(maxHeap.peek()!)
364+
if maxQ.count > minQ.count {
365+
return Double(maxQ.peek()!)
235366
}
236-
return (Double(maxHeap.peek()!) + Double(minHeap.peek()!)) / 2.0
367+
return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0
237368
}
238369
}
239370

@@ -318,6 +449,13 @@ struct Heap<T> {
318449
return 2 * index + 2
319450
}
320451
}
452+
453+
/**
454+
* Your MedianFinder object will be instantiated and called as such:
455+
* let obj = MedianFinder()
456+
* obj.addNum(num)
457+
* let ret_2: Double = obj.findMedian()
458+
*/
321459
```
322460

323461
<!-- tabs:end -->

0 commit comments

Comments
 (0)