最直觀的方法是先排序再取中位數, 時間複雜度 \(o(n\log n)\). 然而最近才得知中位數有時間複雜度 \(o(n)\) 的演算法, 事實上任意順序統計量都可以用 \(o(n)\) 時間找出.
記待排序的數列 \(a = [a_0, \dots, a_]\), 其子列 \(a[p, \dots, r] = [a_p, \dots, a_r]\). 類似快排的方法, 找第 \(i\) 小的元素.
def partition(a, p, r):
pivot = a[r]
i = p - 1
for j in range(p, r):
if a[j] <= pivot:
i += 1
a[i], a[j] = a[j], a[i]
i += 1
a[i], a[r] = a[r], a[i]
return i
def randomized_partition(a, p, r):
i = random.randint(p, r)
a[i], a[r] = a[r], a[i]
return partition(a, p, r)
def randomized_select(a, p, r, i):
if p == r:
return a[p]
q = randomized_partition(a, p, r)
k = q - p + 1
if i == k:
# the pivot value is the answer
return a[q]
elif i < k:
return randomized_select(a, p, q-1, i)
else:
return randomized_select(a, q+1, r, i-k)
記時間複雜度為 \(t(n)\), 以及 \(y = \#a[p, \dots, r]\) 為子列的元素個數.
\[\begin
\mathbb e t(n) &\le \mathbb e\left[t\left(\max(y-1,n-y)\right) + o(n)\right]\\
& = \sum_^n \frac1n\mathbb e\left[ t\left(\max(k-1,n-k)\right) \right] + o(n)\\
& \le \frac2n\sum_^\mathbb et(k) + o(n).
\end
\]之後易證 (substitution method) \(\mathbb et(n) = o(n)\). 不過 worst-case 是 \(o(n^2)\).
依然是從 \(n\) 個元素的數列中找第 \(i\) 小的元素. 不妨約定, 當偶數個元素時, 中位數取中間兩個數中較小的那個.
演算法記為select
演算法, 總體和前乙個演算法一樣, 關鍵是找到乙個好的 pivot. 記時間複雜度為 \(t(n)\).
把數列分成 \(\lceil n/5\rceil\) 組, 每組 5 個, 最後一組可能不足 5 個. 用時 \(o(n)\).
找到每組 5 個元素的中位數. 用時 \(o(n)\).
(遞迴地) 用select
演算法找到 \(\lceil n/5\rceil\) 個中位數的中位數 \(x\). 用時 \(t(\lceil n/5\rceil)\).
以 \(x\) 為 pivot 進行劃分 (partition), 記 \(x\) 為第 \(k\) 小的元素. 用時 \(o(n)\).
若 \(i = k\), 返回 \(x\); 若 \(i < k\), (遞迴地) 用 select 演算法在較小的 partition 中找第 \(i\) 小的元素; 若 \(i > k\), 則在較大的 partition 中找第 \(i - k\) 小的元素.
下面考慮第 5 步的用時. 考慮比 \(x\) 大的元素個數, 有一半的組, 每組 3 個元素比 \(x\) 大 (除了 \(x\) 所在的組和最後乙個不滿 5 個元素的組以外). 故比 \(x\) 大的元素個數至少有
\[3\left( \left\lceil \frac12 \left\lceil\frac\right\rceil \right\rceil -2 \right) \ge \frac - 6.
\]故第 5 步最多用時 \(t(7n/10 + 6)\). 因此
\[t(n) \le t(\lceil n/5\rceil) + t(7n/10 + 6) + o(n).
\]易證 \(t(n) = o(n)\).
注意到若分為每組 3 個, 則不能如上證明 \(t(n) = o(n)\).
leiserson, c. e., rivest, r. l., cormen, t. h., & stein, c. (2009). introduction to algorithms (3rd ed.) (pp. 215-222). cambridge, ma: mit press.
海量資料找到中位數 大堆 小堆
我們分別定義a max,a count,b min,b count a max 表示位元組流較小的那一半數字所組成的大頂堆 記為堆a 的堆頂 a count 表示上述大頂堆元素個數 b min 表示位元組流較大的那一半數字所組成的小頂堆 記為堆b 的堆頂 b count 表示上述小頂堆元素的個數 其...
中位數的中位數
參照王曉東的演算法設計 中位數的中位數,即將一串數分成n段,求其排好序了的中間那個數,再把這些所有中位數再求一次中位數。for int i 0 i r p 4 5 i 找中位數的中位數,r p 4即上面所說的n 5 int x lineselect a,p,p r p 4 5,r p 4 10 線性...
隨時找到資料流的中位數
題目 有乙個源源不斷地吐出整數的資料流,假設你有足夠的空間來儲存吐出的數。請設計乙個名叫medianholder的結構,medianholder可以隨時取得之前吐出所有數的中位數。要求 1 如果medianholder已經儲存了吐出的n個數,那麼任意時刻將乙個新數加入到medianholder的過程...