diff --git a/lcci/17.20.Continuous Median/README.md b/lcci/17.20.Continuous Median/README.md index b3074347e30ca..dd85a2ce6a2a9 100644 --- a/lcci/17.20.Continuous Median/README.md +++ b/lcci/17.20.Continuous Median/README.md @@ -46,17 +46,15 @@ findMedian() -> 2 -### 方法一:优先队列(双堆) +### 方法一:大小根堆(优先队列) -创建大根堆、小根堆,其中:大根堆存放较小的一半元素,小根堆存放较大的一半元素。 +我们可以使用两个堆来维护所有的元素,一个小根堆 $\text{minQ}$ 和一个大根堆 $\text{maxQ}$,其中小根堆 $\text{minQ}$ 存储较大的一半,大根堆 $\text{maxQ}$ 存储较小的一半。 -添加元素时,先放入小根堆,然后将小根堆对顶元素弹出并放入大根堆(使得大根堆个数多 $1$);若大小根堆元素个数差超过 $1$,则将大根堆元素弹出放入小根堆。 +调用 `addNum` 方法时,我们首先将元素加入到大根堆 $\text{maxQ}$,然后将 $\text{maxQ}$ 的堆顶元素弹出并加入到小根堆 $\text{minQ}$。如果此时 $\text{minQ}$ 的大小与 $\text{maxQ}$ 的大小差值大于 $1$,我们就将 $\text{minQ}$ 的堆顶元素弹出并加入到 $\text{maxQ}$。时间复杂度为 $O(\log n)$。 -取中位数时,若大根堆元素较多,取大根堆堆顶,否则取两堆顶元素和的平均值。 +调用 `findMedian` 方法时,如果 $\text{minQ}$ 的大小等于 $\text{maxQ}$ 的大小,说明元素的总数为偶数,我们就可以返回 $\text{minQ}$ 的堆顶元素与 $\text{maxQ}$ 的堆顶元素的平均值;否则,我们返回 $\text{minQ}$ 的堆顶元素。时间复杂度为 $O(1)$。 -**时间复杂度分析:** - -每次添加元素的时间复杂度为 $O(\log n)$,取中位数的时间复杂度为 $O(1)$。 +空间复杂度为 $O(n)$。其中 $n$ 为元素的个数。 @@ -64,23 +62,20 @@ findMedian() -> 2 ```python class MedianFinder: + def __init__(self): - """ - initialize your data structure here. - """ - self.h1 = [] - self.h2 = [] + self.minq = [] + self.maxq = [] def addNum(self, num: int) -> None: - heappush(self.h1, num) - heappush(self.h2, -heappop(self.h1)) - if len(self.h2) - len(self.h1) > 1: - heappush(self.h1, -heappop(self.h2)) + heappush(self.minq, -heappushpop(self.maxq, -num)) + if len(self.minq) - len(self.maxq) > 1: + heappush(self.maxq, -heappop(self.minq)) def findMedian(self) -> float: - if len(self.h2) > len(self.h1): - return -self.h2[0] - return (self.h1[0] - self.h2[0]) / 2 + if len(self.minq) == len(self.maxq): + return (self.minq[0] - self.maxq[0]) / 2 + return self.minq[0] # Your MedianFinder object will be instantiated and called as such: @@ -93,26 +88,22 @@ class MedianFinder: ```java class MedianFinder { - private PriorityQueue q1 = new PriorityQueue<>(); - private PriorityQueue q2 = new PriorityQueue<>(Collections.reverseOrder()); + private PriorityQueue minQ = new PriorityQueue<>(); + private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); - /** initialize your data structure here. */ public MedianFinder() { } public void addNum(int num) { - q1.offer(num); - q2.offer(q1.poll()); - if (q2.size() - q1.size() > 1) { - q1.offer(q2.poll()); + maxQ.offer(num); + minQ.offer(maxQ.poll()); + if (minQ.size() - maxQ.size() > 1) { + maxQ.offer(minQ.poll()); } } public double findMedian() { - if (q2.size() > q1.size()) { - return q2.peek(); - } - return (q1.peek() + q2.peek()) * 1.0 / 2; + return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek(); } } @@ -129,30 +120,27 @@ class MedianFinder { ```cpp class MedianFinder { public: - /** initialize your data structure here. */ MedianFinder() { } void addNum(int num) { - q1.push(num); - q2.push(q1.top()); - q1.pop(); - if (q2.size() - q1.size() > 1) { - q1.push(q2.top()); - q2.pop(); + maxQ.push(num); + minQ.push(maxQ.top()); + maxQ.pop(); + + if (minQ.size() > maxQ.size() + 1) { + maxQ.push(minQ.top()); + minQ.pop(); } } double findMedian() { - if (q2.size() > q1.size()) { - return q2.top(); - } - return (double) (q1.top() + q2.top()) / 2; + return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top(); } private: - priority_queue, greater> q1; - priority_queue q2; + priority_queue maxQ; + priority_queue, greater> minQ; }; /** @@ -167,37 +155,31 @@ private: ```go type MedianFinder struct { - q1 hp - q2 hp + minq hp + maxq hp } -/** initialize your data structure here. */ func Constructor() MedianFinder { return MedianFinder{hp{}, hp{}} } func (this *MedianFinder) AddNum(num int) { - heap.Push(&this.q1, num) - heap.Push(&this.q2, -heap.Pop(&this.q1).(int)) - if this.q2.Len()-this.q1.Len() > 1 { - heap.Push(&this.q1, -heap.Pop(&this.q2).(int)) + minq, maxq := &this.minq, &this.maxq + heap.Push(maxq, -num) + heap.Push(minq, -heap.Pop(maxq).(int)) + if minq.Len()-maxq.Len() > 1 { + heap.Push(maxq, -heap.Pop(minq).(int)) } } func (this *MedianFinder) FindMedian() float64 { - if this.q2.Len() > this.q1.Len() { - return -float64(this.q2.IntSlice[0]) + minq, maxq := this.minq, this.maxq + if minq.Len() == maxq.Len() { + return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2 } - return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0 + return float64(minq.IntSlice[0]) } -/** - * Your MedianFinder object will be instantiated and called as such: - * obj := Constructor(); - * obj.AddNum(num); - * param_2 := obj.FindMedian(); - */ - type hp struct{ sort.IntSlice } func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } @@ -208,32 +190,181 @@ func (h *hp) Pop() any { h.IntSlice = a[:len(a)-1] return v } + +/** + * Your MedianFinder object will be instantiated and called as such: + * obj := Constructor(); + * obj.AddNum(num); + * param_2 := obj.FindMedian(); + */ +``` + +#### TypeScript + +```ts +class MedianFinder { + #minQ = new MinPriorityQueue(); + #maxQ = new MaxPriorityQueue(); + + addNum(num: number): void { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + maxQ.enqueue(num); + minQ.enqueue(maxQ.dequeue().element); + if (minQ.size() - maxQ.size() > 1) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + if (minQ.size() === maxQ.size()) { + return (minQ.front().element + maxQ.front().element) / 2; + } + return minQ.front().element; + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ +``` + +#### Rust + +```rust +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +struct MedianFinder { + minQ: BinaryHeap>, + maxQ: BinaryHeap, +} + +impl MedianFinder { + fn new() -> Self { + MedianFinder { + minQ: BinaryHeap::new(), + maxQ: BinaryHeap::new(), + } + } + + fn add_num(&mut self, num: i32) { + self.maxQ.push(num); + self.minQ.push(Reverse(self.maxQ.pop().unwrap())); + + if self.minQ.len() > self.maxQ.len() + 1 { + self.maxQ.push(self.minQ.pop().unwrap().0); + } + } + + fn find_median(&self) -> f64 { + if self.minQ.len() == self.maxQ.len() { + let min_top = self.minQ.peek().unwrap().0; + let max_top = *self.maxQ.peek().unwrap(); + (min_top + max_top) as f64 / 2.0 + } else { + self.minQ.peek().unwrap().0 as f64 + } + } +} +``` + +#### JavaScript + +```js +var MedianFinder = function () { + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); +}; + +/** + * @param {number} num + * @return {void} + */ +MedianFinder.prototype.addNum = function (num) { + this.maxQ.enqueue(num); + this.minQ.enqueue(this.maxQ.dequeue().element); + if (this.minQ.size() - this.maxQ.size() > 1) { + this.maxQ.enqueue(this.minQ.dequeue().element); + } +}; + +/** + * @return {number} + */ +MedianFinder.prototype.findMedian = function () { + if (this.minQ.size() === this.maxQ.size()) { + return (this.minQ.front().element + this.maxQ.front().element) / 2; + } + return this.minQ.front().element; +}; + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ +``` + +#### C# + +```cs +public class MedianFinder { + private PriorityQueue minQ = new PriorityQueue(); + private PriorityQueue maxQ = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + + public MedianFinder() { + + } + + public void AddNum(int num) { + maxQ.Enqueue(num, num); + minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue()); + if (minQ.Count > maxQ.Count + 1) { + maxQ.Enqueue(minQ.Peek(), minQ.Dequeue()); + } + } + + public double FindMedian() { + return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek(); + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.AddNum(num); + * double param_2 = obj.FindMedian(); + */ ``` #### Swift ```swift class MedianFinder { - private var minHeap = Heap(sort: <) - private var maxHeap = Heap(sort: >) + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) init() { } func addNum(_ num: Int) { - maxHeap.insert(num) - minHeap.insert(maxHeap.remove()!) - - if maxHeap.count < minHeap.count { - maxHeap.insert(minHeap.remove()!) + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) } } func findMedian() -> Double { - if maxHeap.count > minHeap.count { - return Double(maxHeap.peek()!) + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) } - return (Double(maxHeap.peek()!) + Double(minHeap.peek()!)) / 2.0 + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 } } @@ -318,6 +449,13 @@ struct Heap { return 2 * index + 2 } } + +/** + * Your MedianFinder object will be instantiated and called as such: + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() + */ ``` diff --git a/lcci/17.20.Continuous Median/README_EN.md b/lcci/17.20.Continuous Median/README_EN.md index 9fa0d7acfba2a..132be30ebad8f 100644 --- a/lcci/17.20.Continuous Median/README_EN.md +++ b/lcci/17.20.Continuous Median/README_EN.md @@ -53,7 +53,15 @@ findMedian() -> 2 -### Solution 1 +### Solution 1: Min Heap and Max Heap (Priority Queue) + +We can use two heaps to maintain all the elements, a min heap $\text{minQ}$ and a max heap $\text{maxQ}$, where the min heap $\text{minQ}$ stores the larger half, and the max heap $\text{maxQ}$ stores the smaller half. + +When calling the `addNum` method, we first add the element to the max heap $\text{maxQ}$, then pop the top element of $\text{maxQ}$ and add it to the min heap $\text{minQ}$. If at this time the size difference between $\text{minQ}$ and $\text{maxQ}$ is greater than $1$, we pop the top element of $\text{minQ}$ and add it to $\text{maxQ}$. The time complexity is $O(\log n)$. + +When calling the `findMedian` method, if the size of $\text{minQ}$ is equal to the size of $\text{maxQ}$, it means the total number of elements is even, and we can return the average value of the top elements of $\text{minQ}$ and $\text{maxQ}$; otherwise, we return the top element of $\text{minQ}$. The time complexity is $O(1)$. + +The space complexity is $O(n)$, where $n$ is the number of elements. @@ -61,23 +69,20 @@ findMedian() -> 2 ```python class MedianFinder: + def __init__(self): - """ - initialize your data structure here. - """ - self.h1 = [] - self.h2 = [] + self.minq = [] + self.maxq = [] def addNum(self, num: int) -> None: - heappush(self.h1, num) - heappush(self.h2, -heappop(self.h1)) - if len(self.h2) - len(self.h1) > 1: - heappush(self.h1, -heappop(self.h2)) + heappush(self.minq, -heappushpop(self.maxq, -num)) + if len(self.minq) - len(self.maxq) > 1: + heappush(self.maxq, -heappop(self.minq)) def findMedian(self) -> float: - if len(self.h2) > len(self.h1): - return -self.h2[0] - return (self.h1[0] - self.h2[0]) / 2 + if len(self.minq) == len(self.maxq): + return (self.minq[0] - self.maxq[0]) / 2 + return self.minq[0] # Your MedianFinder object will be instantiated and called as such: @@ -90,26 +95,22 @@ class MedianFinder: ```java class MedianFinder { - private PriorityQueue q1 = new PriorityQueue<>(); - private PriorityQueue q2 = new PriorityQueue<>(Collections.reverseOrder()); + private PriorityQueue minQ = new PriorityQueue<>(); + private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); - /** initialize your data structure here. */ public MedianFinder() { } public void addNum(int num) { - q1.offer(num); - q2.offer(q1.poll()); - if (q2.size() - q1.size() > 1) { - q1.offer(q2.poll()); + maxQ.offer(num); + minQ.offer(maxQ.poll()); + if (minQ.size() - maxQ.size() > 1) { + maxQ.offer(minQ.poll()); } } public double findMedian() { - if (q2.size() > q1.size()) { - return q2.peek(); - } - return (q1.peek() + q2.peek()) * 1.0 / 2; + return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek(); } } @@ -126,30 +127,27 @@ class MedianFinder { ```cpp class MedianFinder { public: - /** initialize your data structure here. */ MedianFinder() { } void addNum(int num) { - q1.push(num); - q2.push(q1.top()); - q1.pop(); - if (q2.size() - q1.size() > 1) { - q1.push(q2.top()); - q2.pop(); + maxQ.push(num); + minQ.push(maxQ.top()); + maxQ.pop(); + + if (minQ.size() > maxQ.size() + 1) { + maxQ.push(minQ.top()); + minQ.pop(); } } double findMedian() { - if (q2.size() > q1.size()) { - return q2.top(); - } - return (double) (q1.top() + q2.top()) / 2; + return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top(); } private: - priority_queue, greater> q1; - priority_queue q2; + priority_queue maxQ; + priority_queue, greater> minQ; }; /** @@ -164,37 +162,31 @@ private: ```go type MedianFinder struct { - q1 hp - q2 hp + minq hp + maxq hp } -/** initialize your data structure here. */ func Constructor() MedianFinder { return MedianFinder{hp{}, hp{}} } func (this *MedianFinder) AddNum(num int) { - heap.Push(&this.q1, num) - heap.Push(&this.q2, -heap.Pop(&this.q1).(int)) - if this.q2.Len()-this.q1.Len() > 1 { - heap.Push(&this.q1, -heap.Pop(&this.q2).(int)) + minq, maxq := &this.minq, &this.maxq + heap.Push(maxq, -num) + heap.Push(minq, -heap.Pop(maxq).(int)) + if minq.Len()-maxq.Len() > 1 { + heap.Push(maxq, -heap.Pop(minq).(int)) } } func (this *MedianFinder) FindMedian() float64 { - if this.q2.Len() > this.q1.Len() { - return -float64(this.q2.IntSlice[0]) + minq, maxq := this.minq, this.maxq + if minq.Len() == maxq.Len() { + return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2 } - return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0 + return float64(minq.IntSlice[0]) } -/** - * Your MedianFinder object will be instantiated and called as such: - * obj := Constructor(); - * obj.AddNum(num); - * param_2 := obj.FindMedian(); - */ - type hp struct{ sort.IntSlice } func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } @@ -205,32 +197,181 @@ func (h *hp) Pop() any { h.IntSlice = a[:len(a)-1] return v } + +/** + * Your MedianFinder object will be instantiated and called as such: + * obj := Constructor(); + * obj.AddNum(num); + * param_2 := obj.FindMedian(); + */ +``` + +#### TypeScript + +```ts +class MedianFinder { + #minQ = new MinPriorityQueue(); + #maxQ = new MaxPriorityQueue(); + + addNum(num: number): void { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + maxQ.enqueue(num); + minQ.enqueue(maxQ.dequeue().element); + if (minQ.size() - maxQ.size() > 1) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + if (minQ.size() === maxQ.size()) { + return (minQ.front().element + maxQ.front().element) / 2; + } + return minQ.front().element; + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ +``` + +#### Rust + +```rust +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +struct MedianFinder { + minQ: BinaryHeap>, + maxQ: BinaryHeap, +} + +impl MedianFinder { + fn new() -> Self { + MedianFinder { + minQ: BinaryHeap::new(), + maxQ: BinaryHeap::new(), + } + } + + fn add_num(&mut self, num: i32) { + self.maxQ.push(num); + self.minQ.push(Reverse(self.maxQ.pop().unwrap())); + + if self.minQ.len() > self.maxQ.len() + 1 { + self.maxQ.push(self.minQ.pop().unwrap().0); + } + } + + fn find_median(&self) -> f64 { + if self.minQ.len() == self.maxQ.len() { + let min_top = self.minQ.peek().unwrap().0; + let max_top = *self.maxQ.peek().unwrap(); + (min_top + max_top) as f64 / 2.0 + } else { + self.minQ.peek().unwrap().0 as f64 + } + } +} +``` + +#### JavaScript + +```js +var MedianFinder = function () { + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); +}; + +/** + * @param {number} num + * @return {void} + */ +MedianFinder.prototype.addNum = function (num) { + this.maxQ.enqueue(num); + this.minQ.enqueue(this.maxQ.dequeue().element); + if (this.minQ.size() - this.maxQ.size() > 1) { + this.maxQ.enqueue(this.minQ.dequeue().element); + } +}; + +/** + * @return {number} + */ +MedianFinder.prototype.findMedian = function () { + if (this.minQ.size() === this.maxQ.size()) { + return (this.minQ.front().element + this.maxQ.front().element) / 2; + } + return this.minQ.front().element; +}; + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ +``` + +#### C# + +```cs +public class MedianFinder { + private PriorityQueue minQ = new PriorityQueue(); + private PriorityQueue maxQ = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + + public MedianFinder() { + + } + + public void AddNum(int num) { + maxQ.Enqueue(num, num); + minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue()); + if (minQ.Count > maxQ.Count + 1) { + maxQ.Enqueue(minQ.Peek(), minQ.Dequeue()); + } + } + + public double FindMedian() { + return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek(); + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.AddNum(num); + * double param_2 = obj.FindMedian(); + */ ``` #### Swift ```swift class MedianFinder { - private var minHeap = Heap(sort: <) - private var maxHeap = Heap(sort: >) + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) init() { } func addNum(_ num: Int) { - maxHeap.insert(num) - minHeap.insert(maxHeap.remove()!) - - if maxHeap.count < minHeap.count { - maxHeap.insert(minHeap.remove()!) + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) } } func findMedian() -> Double { - if maxHeap.count > minHeap.count { - return Double(maxHeap.peek()!) + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) } - return (Double(maxHeap.peek()!) + Double(minHeap.peek()!)) / 2.0 + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 } } @@ -315,6 +456,13 @@ struct Heap { return 2 * index + 2 } } + +/** + * Your MedianFinder object will be instantiated and called as such: + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() + */ ``` diff --git a/lcci/17.20.Continuous Median/Solution.cpp b/lcci/17.20.Continuous Median/Solution.cpp index 4e65d4c2bd94f..4db5d9a118f29 100644 --- a/lcci/17.20.Continuous Median/Solution.cpp +++ b/lcci/17.20.Continuous Median/Solution.cpp @@ -1,29 +1,26 @@ class MedianFinder { public: - /** initialize your data structure here. */ MedianFinder() { } void addNum(int num) { - q1.push(num); - q2.push(q1.top()); - q1.pop(); - if (q2.size() - q1.size() > 1) { - q1.push(q2.top()); - q2.pop(); + maxQ.push(num); + minQ.push(maxQ.top()); + maxQ.pop(); + + if (minQ.size() > maxQ.size() + 1) { + maxQ.push(minQ.top()); + minQ.pop(); } } double findMedian() { - if (q2.size() > q1.size()) { - return q2.top(); - } - return (double) (q1.top() + q2.top()) / 2; + return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top(); } private: - priority_queue, greater> q1; - priority_queue q2; + priority_queue maxQ; + priority_queue, greater> minQ; }; /** @@ -31,4 +28,4 @@ class MedianFinder { * MedianFinder* obj = new MedianFinder(); * obj->addNum(num); * double param_2 = obj->findMedian(); - */ \ No newline at end of file + */ diff --git a/lcci/17.20.Continuous Median/Solution.cs b/lcci/17.20.Continuous Median/Solution.cs new file mode 100644 index 0000000000000..6f5d6400527d3 --- /dev/null +++ b/lcci/17.20.Continuous Median/Solution.cs @@ -0,0 +1,27 @@ +public class MedianFinder { + private PriorityQueue minQ = new PriorityQueue(); + private PriorityQueue maxQ = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); + + public MedianFinder() { + + } + + public void AddNum(int num) { + maxQ.Enqueue(num, num); + minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue()); + if (minQ.Count > maxQ.Count + 1) { + maxQ.Enqueue(minQ.Peek(), minQ.Dequeue()); + } + } + + public double FindMedian() { + return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek(); + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.AddNum(num); + * double param_2 = obj.FindMedian(); + */ diff --git a/lcci/17.20.Continuous Median/Solution.go b/lcci/17.20.Continuous Median/Solution.go index 08bef8dd85dc0..5da36e5deebd5 100644 --- a/lcci/17.20.Continuous Median/Solution.go +++ b/lcci/17.20.Continuous Median/Solution.go @@ -1,35 +1,29 @@ type MedianFinder struct { - q1 hp - q2 hp + minq hp + maxq hp } -/** initialize your data structure here. */ func Constructor() MedianFinder { return MedianFinder{hp{}, hp{}} } func (this *MedianFinder) AddNum(num int) { - heap.Push(&this.q1, num) - heap.Push(&this.q2, -heap.Pop(&this.q1).(int)) - if this.q2.Len()-this.q1.Len() > 1 { - heap.Push(&this.q1, -heap.Pop(&this.q2).(int)) + minq, maxq := &this.minq, &this.maxq + heap.Push(maxq, -num) + heap.Push(minq, -heap.Pop(maxq).(int)) + if minq.Len()-maxq.Len() > 1 { + heap.Push(maxq, -heap.Pop(minq).(int)) } } func (this *MedianFinder) FindMedian() float64 { - if this.q2.Len() > this.q1.Len() { - return -float64(this.q2.IntSlice[0]) + minq, maxq := this.minq, this.maxq + if minq.Len() == maxq.Len() { + return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2 } - return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0 + return float64(minq.IntSlice[0]) } -/** - * Your MedianFinder object will be instantiated and called as such: - * obj := Constructor(); - * obj.AddNum(num); - * param_2 := obj.FindMedian(); - */ - type hp struct{ sort.IntSlice } func (h hp) Less(i, j int) bool { return h.IntSlice[i] < h.IntSlice[j] } @@ -39,4 +33,11 @@ func (h *hp) Pop() any { v := a[len(a)-1] h.IntSlice = a[:len(a)-1] return v -} \ No newline at end of file +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * obj := Constructor(); + * obj.AddNum(num); + * param_2 := obj.FindMedian(); + */ diff --git a/lcci/17.20.Continuous Median/Solution.java b/lcci/17.20.Continuous Median/Solution.java index 67cba760f5d1f..4fedda85b0558 100644 --- a/lcci/17.20.Continuous Median/Solution.java +++ b/lcci/17.20.Continuous Median/Solution.java @@ -1,24 +1,20 @@ class MedianFinder { - private PriorityQueue q1 = new PriorityQueue<>(); - private PriorityQueue q2 = new PriorityQueue<>(Collections.reverseOrder()); + private PriorityQueue minQ = new PriorityQueue<>(); + private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); - /** initialize your data structure here. */ public MedianFinder() { } public void addNum(int num) { - q1.offer(num); - q2.offer(q1.poll()); - if (q2.size() - q1.size() > 1) { - q1.offer(q2.poll()); + maxQ.offer(num); + minQ.offer(maxQ.poll()); + if (minQ.size() - maxQ.size() > 1) { + maxQ.offer(minQ.poll()); } } public double findMedian() { - if (q2.size() > q1.size()) { - return q2.peek(); - } - return (q1.peek() + q2.peek()) * 1.0 / 2; + return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek(); } } @@ -27,4 +23,4 @@ public double findMedian() { * MedianFinder obj = new MedianFinder(); * obj.addNum(num); * double param_2 = obj.findMedian(); - */ \ No newline at end of file + */ diff --git a/lcci/17.20.Continuous Median/Solution.js b/lcci/17.20.Continuous Median/Solution.js new file mode 100644 index 0000000000000..5e38a201dc1d8 --- /dev/null +++ b/lcci/17.20.Continuous Median/Solution.js @@ -0,0 +1,33 @@ +var MedianFinder = function () { + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); +}; + +/** + * @param {number} num + * @return {void} + */ +MedianFinder.prototype.addNum = function (num) { + this.maxQ.enqueue(num); + this.minQ.enqueue(this.maxQ.dequeue().element); + if (this.minQ.size() - this.maxQ.size() > 1) { + this.maxQ.enqueue(this.minQ.dequeue().element); + } +}; + +/** + * @return {number} + */ +MedianFinder.prototype.findMedian = function () { + if (this.minQ.size() === this.maxQ.size()) { + return (this.minQ.front().element + this.maxQ.front().element) / 2; + } + return this.minQ.front().element; +}; + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ diff --git a/lcci/17.20.Continuous Median/Solution.py b/lcci/17.20.Continuous Median/Solution.py index 3eb1703cc52f1..0b61b5b78e1c6 100644 --- a/lcci/17.20.Continuous Median/Solution.py +++ b/lcci/17.20.Continuous Median/Solution.py @@ -1,21 +1,18 @@ class MedianFinder: + def __init__(self): - """ - initialize your data structure here. - """ - self.h1 = [] - self.h2 = [] + self.minq = [] + self.maxq = [] def addNum(self, num: int) -> None: - heappush(self.h1, num) - heappush(self.h2, -heappop(self.h1)) - if len(self.h2) - len(self.h1) > 1: - heappush(self.h1, -heappop(self.h2)) + heappush(self.minq, -heappushpop(self.maxq, -num)) + if len(self.minq) - len(self.maxq) > 1: + heappush(self.maxq, -heappop(self.minq)) def findMedian(self) -> float: - if len(self.h2) > len(self.h1): - return -self.h2[0] - return (self.h1[0] - self.h2[0]) / 2 + if len(self.minq) == len(self.maxq): + return (self.minq[0] - self.maxq[0]) / 2 + return self.minq[0] # Your MedianFinder object will be instantiated and called as such: diff --git a/lcci/17.20.Continuous Median/Solution.rs b/lcci/17.20.Continuous Median/Solution.rs new file mode 100644 index 0000000000000..e3a7d37dfb505 --- /dev/null +++ b/lcci/17.20.Continuous Median/Solution.rs @@ -0,0 +1,35 @@ +use std::cmp::Reverse; +use std::collections::BinaryHeap; + +struct MedianFinder { + minQ: BinaryHeap>, + maxQ: BinaryHeap, +} + +impl MedianFinder { + fn new() -> Self { + MedianFinder { + minQ: BinaryHeap::new(), + maxQ: BinaryHeap::new(), + } + } + + fn add_num(&mut self, num: i32) { + self.maxQ.push(num); + self.minQ.push(Reverse(self.maxQ.pop().unwrap())); + + if self.minQ.len() > self.maxQ.len() + 1 { + self.maxQ.push(self.minQ.pop().unwrap().0); + } + } + + fn find_median(&self) -> f64 { + if self.minQ.len() == self.maxQ.len() { + let min_top = self.minQ.peek().unwrap().0; + let max_top = *self.maxQ.peek().unwrap(); + (min_top + max_top) as f64 / 2.0 + } else { + self.minQ.peek().unwrap().0 as f64 + } + } +} diff --git a/lcci/17.20.Continuous Median/Solution.swift b/lcci/17.20.Continuous Median/Solution.swift index edd2293a4803c..bf1b16b0ac17b 100644 --- a/lcci/17.20.Continuous Median/Solution.swift +++ b/lcci/17.20.Continuous Median/Solution.swift @@ -1,24 +1,23 @@ class MedianFinder { - private var minHeap = Heap(sort: <) - private var maxHeap = Heap(sort: >) + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) init() { } func addNum(_ num: Int) { - maxHeap.insert(num) - minHeap.insert(maxHeap.remove()!) - - if maxHeap.count < minHeap.count { - maxHeap.insert(minHeap.remove()!) + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) } } func findMedian() -> Double { - if maxHeap.count > minHeap.count { - return Double(maxHeap.peek()!) + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) } - return (Double(maxHeap.peek()!) + Double(minHeap.peek()!)) / 2.0 + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 } } @@ -102,4 +101,11 @@ struct Heap { private func rightChildIndex(ofParentAt index: Int) -> Int { return 2 * index + 2 } -} \ No newline at end of file +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() + */ diff --git a/lcci/17.20.Continuous Median/Solution.ts b/lcci/17.20.Continuous Median/Solution.ts new file mode 100644 index 0000000000000..0df774deac49f --- /dev/null +++ b/lcci/17.20.Continuous Median/Solution.ts @@ -0,0 +1,28 @@ +class MedianFinder { + #minQ = new MinPriorityQueue(); + #maxQ = new MaxPriorityQueue(); + + addNum(num: number): void { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + maxQ.enqueue(num); + minQ.enqueue(maxQ.dequeue().element); + if (minQ.size() - maxQ.size() > 1) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + if (minQ.size() === maxQ.size()) { + return (minQ.front().element + maxQ.front().element) / 2; + } + return minQ.front().element; + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/README.md" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/README.md" index 3bedc52233d3a..6b34dbdd86dc6 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/README.md" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/README.md" @@ -58,15 +58,15 @@ edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9 -### 方法一:优先队列(大小根堆) +### 方法一:大小根堆(优先队列) -我们可以维护两个优先队列,一个大根堆,一个小根堆,大根堆存储较小的一半数,小根堆存储较大的一半数。 +我们可以使用两个堆来维护所有的元素,一个小根堆 $\text{minQ}$ 和一个大根堆 $\text{maxQ}$,其中小根堆 $\text{minQ}$ 存储较大的一半,大根堆 $\text{maxQ}$ 存储较小的一半。 -当两个堆的元素个数相同时,我们优先往小根堆中添加元素,这样会使得小根堆元素个数比大根堆多 $1$,这样中位数就可以从小根堆中取出。 +调用 `addNum` 方法时,我们首先将元素加入到大根堆 $\text{maxQ}$,然后将 $\text{maxQ}$ 的堆顶元素弹出并加入到小根堆 $\text{minQ}$。如果此时 $\text{minQ}$ 的大小与 $\text{maxQ}$ 的大小差值大于 $1$,我们就将 $\text{minQ}$ 的堆顶元素弹出并加入到 $\text{maxQ}$。时间复杂度为 $O(\log n)$。 -当两个堆的元素个数不同时,说明此时小根堆元素个数比大根堆多 $1$,我们往大根堆中添加元素,这样会使得两个堆元素个数相同,这样中位数就可以从两个堆中取出。 +调用 `findMedian` 方法时,如果 $\text{minQ}$ 的大小等于 $\text{maxQ}$ 的大小,说明元素的总数为偶数,我们就可以返回 $\text{minQ}$ 的堆顶元素与 $\text{maxQ}$ 的堆顶元素的平均值;否则,我们返回 $\text{minQ}$ 的堆顶元素。时间复杂度为 $O(1)$。 -时间复杂度方面,添加元素的时间复杂度为 $O(\log n)$,查找中位数的时间复杂度为 $O(1)$。空间复杂度为 $O(n)$。其中 $n$ 为数据流中元素的个数。 +空间复杂度为 $O(n)$。其中 $n$ 为元素的个数。 @@ -74,23 +74,20 @@ edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9 ```python class MedianFinder: + def __init__(self): - """ - initialize your data structure here. - """ - self.q1 = [] - self.q2 = [] + self.minq = [] + self.maxq = [] def addNum(self, num: int) -> None: - if len(self.q1) > len(self.q2): - heappush(self.q2, -heappushpop(self.q1, num)) - else: - heappush(self.q1, -heappushpop(self.q2, -num)) + heappush(self.minq, -heappushpop(self.maxq, -num)) + if len(self.minq) - len(self.maxq) > 1: + heappush(self.maxq, -heappop(self.minq)) def findMedian(self) -> float: - if len(self.q1) > len(self.q2): - return self.q1[0] - return (self.q1[0] - self.q2[0]) / 2 + if len(self.minq) == len(self.maxq): + return (self.minq[0] - self.maxq[0]) / 2 + return self.minq[0] # Your MedianFinder object will be instantiated and called as such: @@ -103,28 +100,22 @@ class MedianFinder: ```java class MedianFinder { - private PriorityQueue q1 = new PriorityQueue<>(); - private PriorityQueue q2 = new PriorityQueue<>((a, b) -> b - a); + private PriorityQueue minQ = new PriorityQueue<>(); + private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); - /** initialize your data structure here. */ public MedianFinder() { } public void addNum(int num) { - if (q1.size() > q2.size()) { - q1.offer(num); - q2.offer(q1.poll()); - } else { - q2.offer(num); - q1.offer(q2.poll()); + maxQ.offer(num); + minQ.offer(maxQ.poll()); + if (minQ.size() - maxQ.size() > 1) { + maxQ.offer(minQ.poll()); } } public double findMedian() { - if (q1.size() > q2.size()) { - return q1.peek(); - } - return (q1.peek() + q2.peek()) / 2.0; + return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek(); } } @@ -141,32 +132,27 @@ class MedianFinder { ```cpp class MedianFinder { public: - /** initialize your data structure here. */ MedianFinder() { } void addNum(int num) { - if (q1.size() > q2.size()) { - q1.push(num); - q2.push(q1.top()); - q1.pop(); - } else { - q2.push(num); - q1.push(q2.top()); - q2.pop(); + maxQ.push(num); + minQ.push(maxQ.top()); + maxQ.pop(); + + if (minQ.size() > maxQ.size() + 1) { + maxQ.push(minQ.top()); + minQ.pop(); } } double findMedian() { - if (q1.size() > q2.size()) { - return q1.top(); - } - return (q1.top() + q2.top()) / 2.0; + return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top(); } private: - priority_queue, greater> q1; - priority_queue q2; + priority_queue maxQ; + priority_queue, greater> minQ; }; /** @@ -181,29 +167,29 @@ private: ```go type MedianFinder struct { - q1, q2 hp + minq hp + maxq hp } -/** initialize your data structure here. */ func Constructor() MedianFinder { return MedianFinder{hp{}, hp{}} } func (this *MedianFinder) AddNum(num int) { - if this.q1.Len() > this.q2.Len() { - heap.Push(&this.q1, num) - heap.Push(&this.q2, -heap.Pop(&this.q1).(int)) - } else { - heap.Push(&this.q2, -num) - heap.Push(&this.q1, -heap.Pop(&this.q2).(int)) + minq, maxq := &this.minq, &this.maxq + heap.Push(maxq, -num) + heap.Push(minq, -heap.Pop(maxq).(int)) + if minq.Len()-maxq.Len() > 1 { + heap.Push(maxq, -heap.Pop(minq).(int)) } } func (this *MedianFinder) FindMedian() float64 { - if this.q1.Len() > this.q2.Len() { - return float64(this.q1.IntSlice[0]) + minq, maxq := this.minq, this.maxq + if minq.Len() == maxq.Len() { + return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2 } - return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0 + return float64(minq.IntSlice[0]) } type hp struct{ sort.IntSlice } @@ -229,34 +215,24 @@ func (h *hp) Pop() any { ```ts class MedianFinder { - private nums: number[]; - - constructor() { - this.nums = []; - } + #minQ = new MinPriorityQueue(); + #maxQ = new MaxPriorityQueue(); addNum(num: number): void { - const { nums } = this; - let l = 0; - let r = nums.length; - while (l < r) { - const mid = (l + r) >>> 1; - if (nums[mid] < num) { - l = mid + 1; - } else { - r = mid; - } + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + maxQ.enqueue(num); + minQ.enqueue(maxQ.dequeue().element); + if (minQ.size() - maxQ.size() > 1) { + maxQ.enqueue(minQ.dequeue().element); } - nums.splice(l, 0, num); } findMedian(): number { - const { nums } = this; - const n = nums.length; - if ((n & 1) === 1) { - return nums[n >> 1]; + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + if (minQ.size() === maxQ.size()) { + return (minQ.front().element + maxQ.front().element) / 2; } - return (nums[n >> 1] + nums[(n >> 1) - 1]) / 2; + return minQ.front().element; } } @@ -271,40 +247,39 @@ class MedianFinder { #### Rust ```rust +use std::cmp::Reverse; +use std::collections::BinaryHeap; + struct MedianFinder { - nums: Vec, + minQ: BinaryHeap>, + maxQ: BinaryHeap, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MedianFinder { - /** initialize your data structure here. */ fn new() -> Self { - Self { nums: Vec::new() } + MedianFinder { + minQ: BinaryHeap::new(), + maxQ: BinaryHeap::new(), + } } fn add_num(&mut self, num: i32) { - let mut l = 0; - let mut r = self.nums.len(); - while l < r { - let mid = (l + r) >> 1; - if self.nums[mid] < num { - l = mid + 1; - } else { - r = mid; - } + self.maxQ.push(num); + self.minQ.push(Reverse(self.maxQ.pop().unwrap())); + + if self.minQ.len() > self.maxQ.len() + 1 { + self.maxQ.push(self.minQ.pop().unwrap().0); } - self.nums.insert(l, num); } fn find_median(&self) -> f64 { - let n = self.nums.len(); - if (n & 1) == 1 { - return f64::from(self.nums[n >> 1]); + if self.minQ.len() == self.maxQ.len() { + let min_top = self.minQ.peek().unwrap().0; + let max_top = *self.maxQ.peek().unwrap(); + (min_top + max_top) as f64 / 2.0 + } else { + self.minQ.peek().unwrap().0 as f64 } - f64::from(self.nums[n >> 1] + self.nums[(n >> 1) - 1]) / 2.0 } } ``` @@ -312,11 +287,9 @@ impl MedianFinder { #### JavaScript ```js -/** - * initialize your data structure here. - */ var MedianFinder = function () { - this.val = []; + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); }; /** @@ -324,78 +297,52 @@ var MedianFinder = function () { * @return {void} */ MedianFinder.prototype.addNum = function (num) { - let left = 0; - let right = this.val.length; - while (left < right) { - let mid = left + ~~((right - left) / 2); - if (num > this.val[mid]) { - left = mid + 1; - } else { - right = mid; - } + this.maxQ.enqueue(num); + this.minQ.enqueue(this.maxQ.dequeue().element); + if (this.minQ.size() - this.maxQ.size() > 1) { + this.maxQ.enqueue(this.minQ.dequeue().element); } - this.val.splice(left, 0, num); }; /** * @return {number} */ MedianFinder.prototype.findMedian = function () { - let mid = ~~(this.val.length / 2); - return this.val.length % 2 ? this.val[mid] : (this.val[mid - 1] + this.val[mid]) / 2; + if (this.minQ.size() === this.maxQ.size()) { + return (this.minQ.front().element + this.maxQ.front().element) / 2; + } + return this.minQ.front().element; }; + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ ``` #### C# ```cs public class MedianFinder { - private List nums; - private int curIndex; + private PriorityQueue minQ = new PriorityQueue(); + private PriorityQueue maxQ = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); - /** initialize your data structure here. */ public MedianFinder() { - nums = new List(); - } - private int FindIndex(int val) { - int left = 0; - int right = nums.Count - 1; - while (left <= right) { - int mid = left + (right - left) / 2; - if (val > nums[mid]) { - left = mid + 1; - } else { - right = mid - 1; - } - } - return left; } public void AddNum(int num) { - if (nums.Count == 0) { - nums.Add(num); - curIndex = 0; - } else { - curIndex = FindIndex(num); - if (curIndex == nums.Count) { - nums.Add(num); - } else { - nums.Insert(curIndex, num); - } + maxQ.Enqueue(num, num); + minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue()); + if (minQ.Count > maxQ.Count + 1) { + maxQ.Enqueue(minQ.Peek(), minQ.Dequeue()); } } public double FindMedian() { - if (nums.Count % 2 == 1) { - return (double)nums[nums.Count / 2]; - } else { - if (nums.Count == 0) { - return 0; - } else { - return (double) (nums[nums.Count / 2 - 1] + nums[nums.Count / 2]) / 2; - } - } + return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek(); } } @@ -411,31 +358,25 @@ public class MedianFinder { ```swift class MedianFinder { - private var lowerHalf = Heap(sort: >) - private var upperHalf = Heap(sort: <) + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) - init() {} + init() { + } func addNum(_ num: Int) { - if lowerHalf.count == 0 || num <= lowerHalf.peek()! { - lowerHalf.insert(num) - } else { - upperHalf.insert(num) - } - - if lowerHalf.count > upperHalf.count + 1 { - upperHalf.insert(lowerHalf.remove()!) - } else if upperHalf.count > lowerHalf.count { - lowerHalf.insert(upperHalf.remove()!) + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) } } func findMedian() -> Double { - if lowerHalf.count > upperHalf.count { - return Double(lowerHalf.peek()!) - } else { - return (Double(lowerHalf.peek()!) + Double(upperHalf.peek()!)) / 2.0 + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) } + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 } } @@ -443,9 +384,18 @@ struct Heap { var elements: [T] let sort: (T, T) -> Bool - init(sort: @escaping (T, T) -> Bool) { + init(sort: @escaping (T, T) -> Bool, elements: [T] = []) { self.sort = sort - self.elements = [] + self.elements = elements + if !elements.isEmpty { + for i in stride(from: elements.count / 2 - 1, through: 0, by: -1) { + siftDown(from: i) + } + } + } + + var isEmpty: Bool { + return elements.isEmpty } var count: Int { @@ -463,36 +413,32 @@ struct Heap { mutating func remove() -> T? { guard !elements.isEmpty else { return nil } - if elements.count == 1 { - return elements.removeLast() - } else { - let value = elements[0] - elements[0] = elements.removeLast() - siftDown(from: 0) - return value - } + elements.swapAt(0, elements.count - 1) + let removedValue = elements.removeLast() + siftDown(from: 0) + return removedValue } private mutating func siftUp(from index: Int) { var child = index - var parent = parentIndex(of: child) + var parent = parentIndex(ofChildAt: child) while child > 0 && sort(elements[child], elements[parent]) { elements.swapAt(child, parent) child = parent - parent = self.parentIndex(of: child) + parent = parentIndex(ofChildAt: child) } } private mutating func siftDown(from index: Int) { var parent = index while true { - let left = leftChildIndex(of: parent) - let right = rightChildIndex(of: parent) + let left = leftChildIndex(ofParentAt: parent) + let right = rightChildIndex(ofParentAt: parent) var candidate = parent - if left < elements.count && sort(elements[left], elements[candidate]) { + if left < count && sort(elements[left], elements[candidate]) { candidate = left } - if right < elements.count && sort(elements[right], elements[candidate]) { + if right < count && sort(elements[right], elements[candidate]) { candidate = right } if candidate == parent { @@ -503,24 +449,24 @@ struct Heap { } } - private func parentIndex(of index: Int) -> Int { + private func parentIndex(ofChildAt index: Int) -> Int { return (index - 1) / 2 } - private func leftChildIndex(of index: Int) -> Int { + private func leftChildIndex(ofParentAt index: Int) -> Int { return 2 * index + 1 } - private func rightChildIndex(of index: Int) -> Int { + private func rightChildIndex(ofParentAt index: Int) -> Int { return 2 * index + 2 } } /** * Your MedianFinder object will be instantiated and called as such: - * let obj = MedianFinder(); - * obj.addNum(num); - * let param_2 = obj.findMedian(); + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() */ ``` @@ -528,47 +474,4 @@ struct Heap { - - -### 方法二:有序列表 - -我们也可以使用一个有序列表来维护数据流中的元素,这样我们就可以直接通过索引来获取中位数。 - -时间复杂度方面,添加元素的时间复杂度为 $O(\log n)$,查找中位数的时间复杂度为 $O(1)$。空间复杂度为 $O(n)$。其中 $n$ 为数据流中元素的个数。 - - - -#### Python3 - -```python -from sortedcontainers import SortedList - - -class MedianFinder: - def __init__(self): - """ - initialize your data structure here. - """ - self.sl = SortedList() - - def addNum(self, num: int) -> None: - self.sl.add(num) - - def findMedian(self) -> float: - n = len(self.sl) - if n & 1: - return self.sl[n // 2] - return (self.sl[(n - 1) // 2] + self.sl[n // 2]) / 2 - - -# Your MedianFinder object will be instantiated and called as such: -# obj = MedianFinder() -# obj.addNum(num) -# param_2 = obj.findMedian() -``` - - - - - diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cpp" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cpp" index 55f74f02ead0c..4db5d9a118f29 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cpp" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cpp" @@ -1,31 +1,26 @@ class MedianFinder { public: - /** initialize your data structure here. */ MedianFinder() { } void addNum(int num) { - if (q1.size() > q2.size()) { - q1.push(num); - q2.push(q1.top()); - q1.pop(); - } else { - q2.push(num); - q1.push(q2.top()); - q2.pop(); + maxQ.push(num); + minQ.push(maxQ.top()); + maxQ.pop(); + + if (minQ.size() > maxQ.size() + 1) { + maxQ.push(minQ.top()); + minQ.pop(); } } double findMedian() { - if (q1.size() > q2.size()) { - return q1.top(); - } - return (q1.top() + q2.top()) / 2.0; + return minQ.size() == maxQ.size() ? (minQ.top() + maxQ.top()) / 2.0 : minQ.top(); } private: - priority_queue, greater> q1; - priority_queue q2; + priority_queue maxQ; + priority_queue, greater> minQ; }; /** @@ -33,4 +28,4 @@ class MedianFinder { * MedianFinder* obj = new MedianFinder(); * obj->addNum(num); * double param_2 = obj->findMedian(); - */ \ No newline at end of file + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cs" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cs" index 9ec974fe07e5f..6f5d6400527d3 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cs" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.cs" @@ -1,50 +1,21 @@ public class MedianFinder { - private List nums; - private int curIndex; + private PriorityQueue minQ = new PriorityQueue(); + private PriorityQueue maxQ = new PriorityQueue(Comparer.Create((a, b) => b.CompareTo(a))); - /** initialize your data structure here. */ public MedianFinder() { - nums = new List(); - } - private int FindIndex(int val) { - int left = 0; - int right = nums.Count - 1; - while (left <= right) { - int mid = left + (right - left) / 2; - if (val > nums[mid]) { - left = mid + 1; - } else { - right = mid - 1; - } - } - return left; } public void AddNum(int num) { - if (nums.Count == 0) { - nums.Add(num); - curIndex = 0; - } else { - curIndex = FindIndex(num); - if (curIndex == nums.Count) { - nums.Add(num); - } else { - nums.Insert(curIndex, num); - } + maxQ.Enqueue(num, num); + minQ.Enqueue(maxQ.Peek(), maxQ.Dequeue()); + if (minQ.Count > maxQ.Count + 1) { + maxQ.Enqueue(minQ.Peek(), minQ.Dequeue()); } } public double FindMedian() { - if (nums.Count % 2 == 1) { - return (double)nums[nums.Count / 2]; - } else { - if (nums.Count == 0) { - return 0; - } else { - return (double) (nums[nums.Count / 2 - 1] + nums[nums.Count / 2]) / 2; - } - } + return minQ.Count == maxQ.Count ? (minQ.Peek() + maxQ.Peek()) / 2.0 : minQ.Peek(); } } diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.go" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.go" index b16e648a7e8f3..5da36e5deebd5 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.go" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.go" @@ -1,27 +1,27 @@ type MedianFinder struct { - q1, q2 hp + minq hp + maxq hp } -/** initialize your data structure here. */ func Constructor() MedianFinder { return MedianFinder{hp{}, hp{}} } func (this *MedianFinder) AddNum(num int) { - if this.q1.Len() > this.q2.Len() { - heap.Push(&this.q1, num) - heap.Push(&this.q2, -heap.Pop(&this.q1).(int)) - } else { - heap.Push(&this.q2, -num) - heap.Push(&this.q1, -heap.Pop(&this.q2).(int)) + minq, maxq := &this.minq, &this.maxq + heap.Push(maxq, -num) + heap.Push(minq, -heap.Pop(maxq).(int)) + if minq.Len()-maxq.Len() > 1 { + heap.Push(maxq, -heap.Pop(minq).(int)) } } func (this *MedianFinder) FindMedian() float64 { - if this.q1.Len() > this.q2.Len() { - return float64(this.q1.IntSlice[0]) + minq, maxq := this.minq, this.maxq + if minq.Len() == maxq.Len() { + return float64(minq.IntSlice[0]-maxq.IntSlice[0]) / 2 } - return float64(this.q1.IntSlice[0]-this.q2.IntSlice[0]) / 2.0 + return float64(minq.IntSlice[0]) } type hp struct{ sort.IntSlice } @@ -40,4 +40,4 @@ func (h *hp) Pop() any { * obj := Constructor(); * obj.AddNum(num); * param_2 := obj.FindMedian(); - */ \ No newline at end of file + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.java" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.java" index a66e8a1d34881..4fedda85b0558 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.java" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.java" @@ -1,26 +1,20 @@ class MedianFinder { - private PriorityQueue q1 = new PriorityQueue<>(); - private PriorityQueue q2 = new PriorityQueue<>((a, b) -> b - a); + private PriorityQueue minQ = new PriorityQueue<>(); + private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); - /** initialize your data structure here. */ public MedianFinder() { } public void addNum(int num) { - if (q1.size() > q2.size()) { - q1.offer(num); - q2.offer(q1.poll()); - } else { - q2.offer(num); - q1.offer(q2.poll()); + maxQ.offer(num); + minQ.offer(maxQ.poll()); + if (minQ.size() - maxQ.size() > 1) { + maxQ.offer(minQ.poll()); } } public double findMedian() { - if (q1.size() > q2.size()) { - return q1.peek(); - } - return (q1.peek() + q2.peek()) / 2.0; + return minQ.size() == maxQ.size() ? (minQ.peek() + maxQ.peek()) / 2.0 : minQ.peek(); } } @@ -29,4 +23,4 @@ public double findMedian() { * MedianFinder obj = new MedianFinder(); * obj.addNum(num); * double param_2 = obj.findMedian(); - */ \ No newline at end of file + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.js" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.js" index 8e67a7a2aa5e1..5e38a201dc1d8 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.js" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.js" @@ -1,8 +1,6 @@ -/** - * initialize your data structure here. - */ var MedianFinder = function () { - this.val = []; + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); }; /** @@ -10,23 +8,26 @@ var MedianFinder = function () { * @return {void} */ MedianFinder.prototype.addNum = function (num) { - let left = 0; - let right = this.val.length; - while (left < right) { - let mid = left + ~~((right - left) / 2); - if (num > this.val[mid]) { - left = mid + 1; - } else { - right = mid; - } + this.maxQ.enqueue(num); + this.minQ.enqueue(this.maxQ.dequeue().element); + if (this.minQ.size() - this.maxQ.size() > 1) { + this.maxQ.enqueue(this.minQ.dequeue().element); } - this.val.splice(left, 0, num); }; /** * @return {number} */ MedianFinder.prototype.findMedian = function () { - let mid = ~~(this.val.length / 2); - return this.val.length % 2 ? this.val[mid] : (this.val[mid - 1] + this.val[mid]) / 2; + if (this.minQ.size() === this.maxQ.size()) { + return (this.minQ.front().element + this.maxQ.front().element) / 2; + } + return this.minQ.front().element; }; + +/** + * Your MedianFinder object will be instantiated and called as such: + * var obj = new MedianFinder() + * obj.addNum(num) + * var param_2 = obj.findMedian() + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.py" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.py" index 072c240a5ae3c..0b61b5b78e1c6 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.py" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.py" @@ -1,21 +1,18 @@ class MedianFinder: + def __init__(self): - """ - initialize your data structure here. - """ - self.q1 = [] - self.q2 = [] + self.minq = [] + self.maxq = [] def addNum(self, num: int) -> None: - if len(self.q1) > len(self.q2): - heappush(self.q2, -heappushpop(self.q1, num)) - else: - heappush(self.q1, -heappushpop(self.q2, -num)) + heappush(self.minq, -heappushpop(self.maxq, -num)) + if len(self.minq) - len(self.maxq) > 1: + heappush(self.maxq, -heappop(self.minq)) def findMedian(self) -> float: - if len(self.q1) > len(self.q2): - return self.q1[0] - return (self.q1[0] - self.q2[0]) / 2 + if len(self.minq) == len(self.maxq): + return (self.minq[0] - self.maxq[0]) / 2 + return self.minq[0] # Your MedianFinder object will be instantiated and called as such: diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.rs" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.rs" index 52b8117baf98c..e3a7d37dfb505 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.rs" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.rs" @@ -1,36 +1,35 @@ +use std::cmp::Reverse; +use std::collections::BinaryHeap; + struct MedianFinder { - nums: Vec, + minQ: BinaryHeap>, + maxQ: BinaryHeap, } -/** - * `&self` means the method takes an immutable reference. - * If you need a mutable reference, change it to `&mut self` instead. - */ impl MedianFinder { - /** initialize your data structure here. */ fn new() -> Self { - Self { nums: Vec::new() } + MedianFinder { + minQ: BinaryHeap::new(), + maxQ: BinaryHeap::new(), + } } fn add_num(&mut self, num: i32) { - let mut l = 0; - let mut r = self.nums.len(); - while l < r { - let mid = (l + r) >> 1; - if self.nums[mid] < num { - l = mid + 1; - } else { - r = mid; - } + self.maxQ.push(num); + self.minQ.push(Reverse(self.maxQ.pop().unwrap())); + + if self.minQ.len() > self.maxQ.len() + 1 { + self.maxQ.push(self.minQ.pop().unwrap().0); } - self.nums.insert(l, num); } fn find_median(&self) -> f64 { - let n = self.nums.len(); - if (n & 1) == 1 { - return f64::from(self.nums[n >> 1]); + if self.minQ.len() == self.maxQ.len() { + let min_top = self.minQ.peek().unwrap().0; + let max_top = *self.maxQ.peek().unwrap(); + (min_top + max_top) as f64 / 2.0 + } else { + self.minQ.peek().unwrap().0 as f64 } - f64::from(self.nums[n >> 1] + self.nums[(n >> 1) - 1]) / 2.0 } } diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.swift" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.swift" index 20d2c1114cce4..bf1b16b0ac17b 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.swift" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.swift" @@ -1,29 +1,23 @@ class MedianFinder { - private var lowerHalf = Heap(sort: >) - private var upperHalf = Heap(sort: <) + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) - init() {} + init() { + } func addNum(_ num: Int) { - if lowerHalf.count == 0 || num <= lowerHalf.peek()! { - lowerHalf.insert(num) - } else { - upperHalf.insert(num) - } - - if lowerHalf.count > upperHalf.count + 1 { - upperHalf.insert(lowerHalf.remove()!) - } else if upperHalf.count > lowerHalf.count { - lowerHalf.insert(upperHalf.remove()!) + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) } } func findMedian() -> Double { - if lowerHalf.count > upperHalf.count { - return Double(lowerHalf.peek()!) - } else { - return (Double(lowerHalf.peek()!) + Double(upperHalf.peek()!)) / 2.0 + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) } + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 } } @@ -31,9 +25,18 @@ struct Heap { var elements: [T] let sort: (T, T) -> Bool - init(sort: @escaping (T, T) -> Bool) { + init(sort: @escaping (T, T) -> Bool, elements: [T] = []) { self.sort = sort - self.elements = [] + self.elements = elements + if !elements.isEmpty { + for i in stride(from: elements.count / 2 - 1, through: 0, by: -1) { + siftDown(from: i) + } + } + } + + var isEmpty: Bool { + return elements.isEmpty } var count: Int { @@ -51,36 +54,32 @@ struct Heap { mutating func remove() -> T? { guard !elements.isEmpty else { return nil } - if elements.count == 1 { - return elements.removeLast() - } else { - let value = elements[0] - elements[0] = elements.removeLast() - siftDown(from: 0) - return value - } + elements.swapAt(0, elements.count - 1) + let removedValue = elements.removeLast() + siftDown(from: 0) + return removedValue } private mutating func siftUp(from index: Int) { var child = index - var parent = parentIndex(of: child) + var parent = parentIndex(ofChildAt: child) while child > 0 && sort(elements[child], elements[parent]) { elements.swapAt(child, parent) child = parent - parent = self.parentIndex(of: child) + parent = parentIndex(ofChildAt: child) } } private mutating func siftDown(from index: Int) { var parent = index while true { - let left = leftChildIndex(of: parent) - let right = rightChildIndex(of: parent) + let left = leftChildIndex(ofParentAt: parent) + let right = rightChildIndex(ofParentAt: parent) var candidate = parent - if left < elements.count && sort(elements[left], elements[candidate]) { + if left < count && sort(elements[left], elements[candidate]) { candidate = left } - if right < elements.count && sort(elements[right], elements[candidate]) { + if right < count && sort(elements[right], elements[candidate]) { candidate = right } if candidate == parent { @@ -91,22 +90,22 @@ struct Heap { } } - private func parentIndex(of index: Int) -> Int { + private func parentIndex(ofChildAt index: Int) -> Int { return (index - 1) / 2 } - private func leftChildIndex(of index: Int) -> Int { + private func leftChildIndex(ofParentAt index: Int) -> Int { return 2 * index + 1 } - private func rightChildIndex(of index: Int) -> Int { + private func rightChildIndex(ofParentAt index: Int) -> Int { return 2 * index + 2 } } /** * Your MedianFinder object will be instantiated and called as such: - * let obj = MedianFinder(); - * obj.addNum(num); - * let param_2 = obj.findMedian(); - */ \ No newline at end of file + * let obj = MedianFinder() + * obj.addNum(num) + * let ret_2: Double = obj.findMedian() + */ diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.ts" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.ts" index 8d02f1c2588a9..0df774deac49f 100644 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.ts" +++ "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution.ts" @@ -1,32 +1,22 @@ class MedianFinder { - private nums: number[]; - - constructor() { - this.nums = []; - } + #minQ = new MinPriorityQueue(); + #maxQ = new MaxPriorityQueue(); addNum(num: number): void { - const { nums } = this; - let l = 0; - let r = nums.length; - while (l < r) { - const mid = (l + r) >>> 1; - if (nums[mid] < num) { - l = mid + 1; - } else { - r = mid; - } + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + maxQ.enqueue(num); + minQ.enqueue(maxQ.dequeue().element); + if (minQ.size() - maxQ.size() > 1) { + maxQ.enqueue(minQ.dequeue().element); } - nums.splice(l, 0, num); } findMedian(): number { - const { nums } = this; - const n = nums.length; - if ((n & 1) === 1) { - return nums[n >> 1]; + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + if (minQ.size() === maxQ.size()) { + return (minQ.front().element + maxQ.front().element) / 2; } - return (nums[n >> 1] + nums[(n >> 1) - 1]) / 2; + return minQ.front().element; } } diff --git "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution2.py" "b/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution2.py" deleted file mode 100644 index ba0aa0cd2532e..0000000000000 --- "a/lcof/\351\235\242\350\257\225\351\242\23041. \346\225\260\346\215\256\346\265\201\344\270\255\347\232\204\344\270\255\344\275\215\346\225\260/Solution2.py" +++ /dev/null @@ -1,24 +0,0 @@ -from sortedcontainers import SortedList - - -class MedianFinder: - def __init__(self): - """ - initialize your data structure here. - """ - self.sl = SortedList() - - def addNum(self, num: int) -> None: - self.sl.add(num) - - def findMedian(self) -> float: - n = len(self.sl) - if n & 1: - return self.sl[n // 2] - return (self.sl[(n - 1) // 2] + self.sl[n // 2]) / 2 - - -# Your MedianFinder object will be instantiated and called as such: -# obj = MedianFinder() -# obj.addNum(num) -# param_2 = obj.findMedian()