295. Find Median from Data Stream
題目網址:https://leetcode.cn/problems/find-median-from-data-stream/
題意:中位數是指有序 array 裡中間的數。若 array 長度偶數, 則中位數是中間兩數的平均值。
arr = [2,3,4]
, 則中位數 =3
arr = [2,3]
, 則中位數 =(2+3) / 2 = 2.5
請實現
MedianFinder
class:
MedianFinder()
:初始化 MedianFindervoid addNum(int num)
:新增num
到資料結構中double findMedian()
:找出目前的中位數
Solution 1:
想法:利用 Balanced BST(紅黑樹), C++ 可直接使用
multiset
, 將元素 push 到 multiset 中它會自動排好序。我們用一個 iteratormid
指向目前的中位數(若 multiset 中的元素個數為偶數, 則讓 mid 指向中間靠左的元素)
- 若
num
加入前,set
中的元素為偶數個(OOXOOO
, 其中X
代表mid
), 則加入nums
後, 分兩種情況:
- 若
nums >= *mid
:OOXOOO
(加入前) ➔OOXOOOO
(加入後), 則要將mid
往右移一位,OOXOOOO
➔OOOXOOO
- 若
nums < *mid
:OOXOOO
(加入前) ➔OOOXOOO
(加入後), 則mid
不需移動- 加入
num
後,set
中的元素變為奇數個, 因此返回mid
即可- 若
num
加入前,set
中的元素為奇數個(OOXOO
), 則加入nums
後, 分兩種情況:
- 若
nums >= *mid
:OOXOO
(加入前) ➔OOXOOO
(加入後), 則mid
不需移動- 若
nums < *mid
:OOXOO
(加入前) ➔OOOXOO
(加入後), 則要將mid
往左移一位,OOOXOO
➔OOXOOO
- 加入
num
後,set
中的元素變為偶數個, 因此要返回中間兩數的平均值
class MedianFinder { |
- time:
addNum()
:$O(log(n))$, Balanced BST 新增元素所需的時間複雜度為 $O(h)$, 因為是 Balanced, 所以 $h = log(n)$findMedian()
:$O(1)$
- space:$O(n)$ ➔
set
Solution 2:
想法:利用 Heap, 使用兩個 heap 來分別記錄中間左中位數、右中位數, 因為當
set
中元素為偶數時, 我們需要取得這兩個數來計算平均值
- Max Heap
smaller
紀錄所有≤ 中位數的數
,smaller.top()
可得到所有≤ 中位數的數
中最大的值, 也就是左中位數- Min Heap
larger
紀錄所有> 中位數的數
,larger.top()
可得到所有> 中位數的數
中最小的值, 也就是右中位數在新增
num
時, 我們必須使兩個 heap 的長度保持平衡, 平衡的定義如下:
- 滿足
0 ≤ | smaller.size() - larger.size() | ≤ 1
當兩個 heap 的長度不平衡時, 則把長度較長者的 top 給 pop 掉, 並把其 push 到另一個 heap
- 要返回中位數時, 我們比較兩個 heap 的長度
若
smaller.size() > larger.size()
時, 返回smaller.top()
e.g.
smaller = [1,2,3]
,larger = [4,5]
, 則中位數 =3
若
larger.size() > smaller.size()
時, 返回larger.top()
e.g.
smaller = [1,2]
,larger = [3,4,5]
, 則中位數 =3
若
smaller.size() == larger.size()
, 代表set
中元素個數為偶數, 返回中間兩數的平均值, 也就是(smaller.top() + larger.top()) / 2.0
e.g.
smaller = [1,2,3]
,larger = [4,5,6]
, 則中位數 =(3 + 4) / 2 = 3.5
class MedianFinder { |
- time:
addNum()
:$O(log(n))$ 從 heap 中新增 or 刪除元素findMedian()
:$O(1)$
- space:$O(n)$ ➔
smaller
+larger
的長度不超過n