From 7f9eeaf08ec196e24484c19dae0f7b66d319b9ce Mon Sep 17 00:00:00 2001 From: rain84 Date: Wed, 26 Jun 2024 22:02:48 +0300 Subject: [PATCH 01/16] feat: add 2nd ts solution to lc problem: No.0295 --- .../README.md | 38 +++++++++++++++++++ .../README_EN.md | 38 +++++++++++++++++++ .../Solution2.ts | 23 +++++++++++ 3 files changed, 99 insertions(+) create mode 100644 solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts 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..44ce09bdc169e 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 @@ -422,4 +422,42 @@ public class MedianFinder { + + +### Solution 2. Min/max heaps + + + +#### 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()) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + const median = + maxQ.size() === minQ.size() + ? (maxQ.front().element + minQ.front().element) / 2 + : maxQ.front().element; + + return median; + } +} +``` + + + + + 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..359d346fcbfe1 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 @@ -417,4 +417,42 @@ public class MedianFinder { + + +### Solution 2. Min/max heaps + + + +#### 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()) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + const median = + maxQ.size() === minQ.size() + ? (maxQ.front().element + minQ.front().element) / 2 + : maxQ.front().element; + + return median; + } +} +``` + + + + + diff --git a/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts b/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts new file mode 100644 index 0000000000000..4b67cb2267685 --- /dev/null +++ b/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts @@ -0,0 +1,23 @@ +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()) { + maxQ.enqueue(minQ.dequeue().element); + } + } + + findMedian(): number { + const [minQ, maxQ] = [this.#minQ, this.#maxQ]; + const median = + maxQ.size() === minQ.size() + ? (maxQ.front().element + minQ.front().element) / 2 + : maxQ.front().element; + + return median; + } +} From 3534e198c33cdf77918e7a319e62758b519c0619 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:23:20 +0800 Subject: [PATCH 02/16] Update README.md --- .../README.md | 383 ++++++++++-------- 1 file changed, 206 insertions(+), 177 deletions(-) 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 44ce09bdc169e..d9c15caab2ad3 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,23 @@ 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 +147,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 +182,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 +217,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 +262,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 +302,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 +312,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,42 +369,120 @@ public class MedianFinder { */ ``` - +#### Swift - +```swift +class MedianFinder { + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) - + init() { + } -### Solution 2. Min/max heaps + 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 + } +} -#### TypeScript +struct Heap { + var elements: [T] + let sort: (T, T) -> Bool -```ts -class MedianFinder { - #minQ = new MinPriorityQueue(); - #maxQ = new MaxPriorityQueue(); + 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) + } + } + } - addNum(num: number): void { - const [minQ, maxQ] = [this.#minQ, this.#maxQ]; - maxQ.enqueue(num); - minQ.enqueue(maxQ.dequeue().element); - if (minQ.size() > maxQ.size()) { - maxQ.enqueue(minQ.dequeue().element); + 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) } } - findMedian(): number { - const [minQ, maxQ] = [this.#minQ, this.#maxQ]; - const median = - maxQ.size() === minQ.size() - ? (maxQ.front().element + minQ.front().element) / 2 - : maxQ.front().element; + 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 + } - return median; + 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() + */ ``` From 59cd174ee7f9d683e3995e4107bc4ff290b9109f Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:23:31 +0800 Subject: [PATCH 03/16] Update README_EN.md --- .../README_EN.md | 381 ++++++++++-------- 1 file changed, 210 insertions(+), 171 deletions(-) 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 359d346fcbfe1..957d11c6a5a67 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,23 @@ 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 +152,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 +187,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 +222,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 +267,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 +307,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 +317,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,42 +374,120 @@ public class MedianFinder { */ ``` - +#### Swift - +```swift +class MedianFinder { + private var minQ = Heap(sort: <) + private var maxQ = Heap(sort: >) - + init() { + } -### Solution 2. Min/max heaps + 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 + } +} -#### TypeScript +struct Heap { + var elements: [T] + let sort: (T, T) -> Bool -```ts -class MedianFinder { - #minQ = new MinPriorityQueue(); - #maxQ = new MaxPriorityQueue(); + 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) + } + } + } - addNum(num: number): void { - const [minQ, maxQ] = [this.#minQ, this.#maxQ]; - maxQ.enqueue(num); - minQ.enqueue(maxQ.dequeue().element); - if (minQ.size() > maxQ.size()) { - maxQ.enqueue(minQ.dequeue().element); + 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) } } - findMedian(): number { - const [minQ, maxQ] = [this.#minQ, this.#maxQ]; - const median = - maxQ.size() === minQ.size() - ? (maxQ.front().element + minQ.front().element) / 2 - : maxQ.front().element; + 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 + } + } - return median; + 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() + */ ``` From 93af2808b062bd1b60eff1bb21922f268108959c Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:23:41 +0800 Subject: [PATCH 04/16] Delete solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts --- .../Solution2.ts | 23 ------------------- 1 file changed, 23 deletions(-) delete mode 100644 solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts diff --git a/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts b/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts deleted file mode 100644 index 4b67cb2267685..0000000000000 --- a/solution/0200-0299/0295.Find Median from Data Stream/Solution2.ts +++ /dev/null @@ -1,23 +0,0 @@ -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()) { - maxQ.enqueue(minQ.dequeue().element); - } - } - - findMedian(): number { - const [minQ, maxQ] = [this.#minQ, this.#maxQ]; - const median = - maxQ.size() === minQ.size() - ? (maxQ.front().element + minQ.front().element) / 2 - : maxQ.front().element; - - return median; - } -} From 6e38e482f1c96e6ecf40729785cc7405f44c808e Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:24:13 +0800 Subject: [PATCH 05/16] Update Solution.py --- .../Solution.py | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) 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: From 816af1ad951802c02a560a793fe891844c6fa596 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:24:24 +0800 Subject: [PATCH 06/16] Update Solution.java --- .../Solution.java | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) 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..c0b04faf7af10 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,21 @@ 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 +24,4 @@ public double findMedian() { * MedianFinder obj = new MedianFinder(); * obj.addNum(num); * double param_2 = obj.findMedian(); - */ \ No newline at end of file + */ From c5636389343d19df11d0e409daecc43e9bf61e0e Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:24:32 +0800 Subject: [PATCH 07/16] Update Solution.cpp --- .../Solution.cpp | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) 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 + */ From 05745f605d24696c9b015de396a2bc9db19ca1a1 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:24:40 +0800 Subject: [PATCH 08/16] Update Solution.go --- .../Solution.go | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) 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(); + */ From 3d377e36d239449fa40c149d8abede7861e1095e Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:24:46 +0800 Subject: [PATCH 09/16] Update Solution.js --- .../Solution.js | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) 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() + */ From b2a524734320796d80a7cfca92c95079c5ad5013 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:25:01 +0800 Subject: [PATCH 10/16] Update Solution.ts --- .../Solution.ts | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) 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; } } From dcc257a30f8b55466204da3e4a9def4ca4a41a55 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:25:08 +0800 Subject: [PATCH 11/16] Update Solution.rs --- .../Solution.rs | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) 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 } } From 5458b9e1343d0c8cfe431a6f3cf5e3afb21d002e Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:25:18 +0800 Subject: [PATCH 12/16] Update Solution.cs --- .../Solution.cs | 43 +++---------------- 1 file changed, 7 insertions(+), 36 deletions(-) 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(); } } From 9b5a2bede9d258b60d21ee1ed033ff5e0bc327b3 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:25:31 +0800 Subject: [PATCH 13/16] Create Solution.swift --- .../Solution.swift | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 solution/0200-0299/0295.Find Median from Data Stream/Solution.swift 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() + */ From 84274e88bd86e98febf7bcd4719c3bd9d95eb16b Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:27:16 +0800 Subject: [PATCH 14/16] Update Solution.java --- .../0200-0299/0295.Find Median from Data Stream/Solution.java | 1 - 1 file changed, 1 deletion(-) 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 c0b04faf7af10..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 @@ -3,7 +3,6 @@ class MedianFinder { private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); public MedianFinder() { - } public void addNum(int num) { From 05caeb00e8d52ce4a8df033bf6136607a6414935 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:27:32 +0800 Subject: [PATCH 15/16] Update README.md --- solution/0200-0299/0295.Find Median from Data Stream/README.md | 1 - 1 file changed, 1 deletion(-) 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 d9c15caab2ad3..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 @@ -118,7 +118,6 @@ class MedianFinder { private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); public MedianFinder() { - } public void addNum(int num) { From f94c8cb17bf6f113a8d52c34c0c32e3837e57bee Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 28 Jun 2024 08:27:48 +0800 Subject: [PATCH 16/16] Update README_EN.md --- .../0200-0299/0295.Find Median from Data Stream/README_EN.md | 1 - 1 file changed, 1 deletion(-) 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 957d11c6a5a67..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 @@ -123,7 +123,6 @@ class MedianFinder { private PriorityQueue maxQ = new PriorityQueue<>(Collections.reverseOrder()); public MedianFinder() { - } public void addNum(int num) {