diff --git a/solution/0200-0299/0295.Find Median from Data Stream/README.md b/solution/0200-0299/0295.Find Median from Data Stream/README.md index 6d493c1a05408..e2121ad724561 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/README.md +++ b/solution/0200-0299/0295.Find Median from Data Stream/README.md @@ -72,17 +72,15 @@ medianFinder.findMedian(); // return 2.0 -### 方法一:优先队列(双堆) +### 方法一:大小根堆(优先队列) -创建大根堆、小根堆,其中:大根堆存放较小的一半元素,小根堆存放较大的一半元素。 +我们可以使用两个堆来维护所有的元素,一个小根堆 $\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$ 为元素的个数。 @@ -90,23 +88,20 @@ medianFinder.findMedian(); // return 2.0 ```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: @@ -119,26 +114,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(); } } @@ -155,30 +146,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; }; /** @@ -193,37 +181,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] } @@ -234,40 +216,37 @@ 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 { - 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; } } @@ -282,40 +261,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 } } ``` @@ -323,11 +301,9 @@ impl MedianFinder { #### JavaScript ```js -/** - * initialize your data structure here. - */ var MedianFinder = function () { - this.val = []; + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); }; /** @@ -335,78 +311,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(); } } @@ -418,6 +368,122 @@ public class MedianFinder { */ ``` +#### Swift + +```swift +class MedianFinder { + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) + + init() { + } + + func addNum(_ num: Int) { + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) + } + } + + func findMedian() -> Double { + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) + } + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 + } +} + +struct Heap { + var elements: [T] + let sort: (T, T) -> Bool + + init(sort: @escaping (T, T) -> Bool, elements: [T] = []) { + self.sort = sort + 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 { + return elements.count + } + + func peek() -> T? { + return elements.first + } + + mutating func insert(_ value: T) { + elements.append(value) + siftUp(from: elements.count - 1) + } + + mutating func remove() -> T? { + guard !elements.isEmpty else { return nil } + 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(ofChildAt: child) + while child > 0 && sort(elements[child], elements[parent]) { + elements.swapAt(child, parent) + child = parent + parent = parentIndex(ofChildAt: child) + } + } + + private mutating func siftDown(from index: Int) { + var parent = index + while true { + let left = leftChildIndex(ofParentAt: parent) + let right = rightChildIndex(ofParentAt: parent) + var candidate = parent + if left < count && sort(elements[left], elements[candidate]) { + candidate = left + } + if right < count && sort(elements[right], elements[candidate]) { + candidate = right + } + if candidate == parent { + return + } + elements.swapAt(parent, candidate) + parent = candidate + } + } + + private func parentIndex(ofChildAt index: Int) -> Int { + return (index - 1) / 2 + } + + private func leftChildIndex(ofParentAt index: Int) -> Int { + return 2 * index + 1 + } + + 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 ret_2: Double = obj.findMedian() + */ +``` + diff --git a/solution/0200-0299/0295.Find Median from Data Stream/README_EN.md b/solution/0200-0299/0295.Find Median from Data Stream/README_EN.md index 90732ca922234..c2787a62043d6 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/README_EN.md +++ b/solution/0200-0299/0295.Find Median from Data Stream/README_EN.md @@ -77,7 +77,15 @@ medianFinder.findMedian(); // return 2.0 -### 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. @@ -85,23 +93,20 @@ medianFinder.findMedian(); // return 2.0 ```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: @@ -114,26 +119,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(); } } @@ -150,30 +151,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; }; /** @@ -188,37 +186,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] } @@ -229,40 +221,37 @@ 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 { - 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; } } @@ -277,40 +266,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 } } ``` @@ -318,11 +306,9 @@ impl MedianFinder { #### JavaScript ```js -/** - * initialize your data structure here. - */ var MedianFinder = function () { - this.val = []; + this.minQ = new MinPriorityQueue(); + this.maxQ = new MaxPriorityQueue(); }; /** @@ -330,78 +316,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(); } } @@ -413,6 +373,122 @@ public class MedianFinder { */ ``` +#### Swift + +```swift +class MedianFinder { + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) + + init() { + } + + func addNum(_ num: Int) { + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) + } + } + + func findMedian() -> Double { + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) + } + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 + } +} + +struct Heap { + var elements: [T] + let sort: (T, T) -> Bool + + init(sort: @escaping (T, T) -> Bool, elements: [T] = []) { + self.sort = sort + 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 { + return elements.count + } + + func peek() -> T? { + return elements.first + } + + mutating func insert(_ value: T) { + elements.append(value) + siftUp(from: elements.count - 1) + } + + mutating func remove() -> T? { + guard !elements.isEmpty else { return nil } + 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(ofChildAt: child) + while child > 0 && sort(elements[child], elements[parent]) { + elements.swapAt(child, parent) + child = parent + parent = parentIndex(ofChildAt: child) + } + } + + private mutating func siftDown(from index: Int) { + var parent = index + while true { + let left = leftChildIndex(ofParentAt: parent) + let right = rightChildIndex(ofParentAt: parent) + var candidate = parent + if left < count && sort(elements[left], elements[candidate]) { + candidate = left + } + if right < count && sort(elements[right], elements[candidate]) { + candidate = right + } + if candidate == parent { + return + } + elements.swapAt(parent, candidate) + parent = candidate + } + } + + private func parentIndex(ofChildAt index: Int) -> Int { + return (index - 1) / 2 + } + + private func leftChildIndex(ofParentAt index: Int) -> Int { + return 2 * index + 1 + } + + 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 ret_2: Double = obj.findMedian() + */ +``` + diff --git a/solution/0200-0299/0295.Find Median from Data Stream/Solution.cpp b/solution/0200-0299/0295.Find Median from Data Stream/Solution.cpp index 4e65d4c2bd94f..4db5d9a118f29 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.cpp +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.cs b/solution/0200-0299/0295.Find Median from Data Stream/Solution.cs index 9ec974fe07e5f..6f5d6400527d3 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.cs +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.go b/solution/0200-0299/0295.Find Median from Data Stream/Solution.go index 08bef8dd85dc0..5da36e5deebd5 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.go +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.java b/solution/0200-0299/0295.Find Median from Data Stream/Solution.java index 67cba760f5d1f..4fedda85b0558 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.java +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.js b/solution/0200-0299/0295.Find Median from Data Stream/Solution.js index 8e67a7a2aa5e1..5e38a201dc1d8 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.js +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.py b/solution/0200-0299/0295.Find Median from Data Stream/Solution.py index 3eb1703cc52f1..0b61b5b78e1c6 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.py +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.rs b/solution/0200-0299/0295.Find Median from Data Stream/Solution.rs index 52b8117baf98c..e3a7d37dfb505 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.rs +++ b/solution/0200-0299/0295.Find Median from Data Stream/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/solution/0200-0299/0295.Find Median from Data Stream/Solution.swift b/solution/0200-0299/0295.Find Median from Data Stream/Solution.swift new file mode 100644 index 0000000000000..bf1b16b0ac17b --- /dev/null +++ b/solution/0200-0299/0295.Find Median from Data Stream/Solution.swift @@ -0,0 +1,111 @@ +class MedianFinder { + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) + + init() { + } + + func addNum(_ num: Int) { + maxQ.insert(num) + minQ.insert(maxQ.remove()!) + if maxQ.count < minQ.count { + maxQ.insert(minQ.remove()!) + } + } + + func findMedian() -> Double { + if maxQ.count > minQ.count { + return Double(maxQ.peek()!) + } + return (Double(maxQ.peek()!) + Double(minQ.peek()!)) / 2.0 + } +} + +struct Heap { + var elements: [T] + let sort: (T, T) -> Bool + + init(sort: @escaping (T, T) -> Bool, elements: [T] = []) { + self.sort = sort + 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 { + return elements.count + } + + func peek() -> T? { + return elements.first + } + + mutating func insert(_ value: T) { + elements.append(value) + siftUp(from: elements.count - 1) + } + + mutating func remove() -> T? { + guard !elements.isEmpty else { return nil } + 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(ofChildAt: child) + while child > 0 && sort(elements[child], elements[parent]) { + elements.swapAt(child, parent) + child = parent + parent = parentIndex(ofChildAt: child) + } + } + + private mutating func siftDown(from index: Int) { + var parent = index + while true { + let left = leftChildIndex(ofParentAt: parent) + let right = rightChildIndex(ofParentAt: parent) + var candidate = parent + if left < count && sort(elements[left], elements[candidate]) { + candidate = left + } + if right < count && sort(elements[right], elements[candidate]) { + candidate = right + } + if candidate == parent { + return + } + elements.swapAt(parent, candidate) + parent = candidate + } + } + + private func parentIndex(ofChildAt index: Int) -> Int { + return (index - 1) / 2 + } + + private func leftChildIndex(ofParentAt index: Int) -> Int { + return 2 * index + 1 + } + + 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 ret_2: Double = obj.findMedian() + */ diff --git a/solution/0200-0299/0295.Find Median from Data Stream/Solution.ts b/solution/0200-0299/0295.Find Median from Data Stream/Solution.ts index 8d02f1c2588a9..0df774deac49f 100644 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution.ts +++ b/solution/0200-0299/0295.Find Median from Data Stream/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; } }