從 5 億個數中找出中位數。資料排序後,位置在最中間的數就是中位數。當樣本數為奇數時,中位數 =(n+1)/2;當樣本數為偶數時,中位數為 n/2 與 1+n/2 的均值。
如果這道題目沒有記憶體大小的限制,則可以把所有的數字排序後找出中位數,但是最好的排序演算法的時間複雜度都是 o(nlogn)(n 為數字的個數)。這裡介紹另外一種求解中位數的演算法——雙堆法。
方法一:雙堆法
這個演算法的主要思路是維護兩個堆,乙個大頂堆,乙個小頂堆,且這兩個堆需要滿足如下兩個特性。
特性一:大頂堆中最大的數小於或等於小頂堆中最小的數。
特性二:保證這兩個堆中的元素個數的差不能超過 1。
若資料總數為偶數時,當這兩個堆建立好以後,中位數顯然就是兩個堆頂元素的平均值。當資料總數為奇數時,根據兩個堆的大小,中位數一定在資料多的堆的堆頂。對本題而言,具體實現思路:維護兩個堆 maxheap 與 minheap,這兩個堆的大小分別為 max_size 和 min_size,然後開始遍歷數字。對於遍歷到的數字 data:
1)如果 data<maxheap 的堆頂元素,此時為了滿足特性 1,只能把 data 插入到 maxheap 中。為了滿足特性二,需要分以下幾種情況討論。
① 如果 max_size≤min_size,則說明大頂堆元素個數小於小頂堆元素個數,此時把 data 直接插入大頂堆中,並把這個堆調整為大頂堆。
② 如果 max_size>min_size,為了保持兩個堆元素個數的差不超過 1,則需要把 maxheap 堆頂的元素移動到 minheap 中,接著把 data 插入到 maxheap 中。同時通過對堆的調整,分別讓兩個堆保持大頂堆與小頂堆的特性。
2)如果 maxheap 堆頂元素 ≤data≤minheap 堆頂元素,則為了滿足特性一,可以把 data 插入任意乙個堆中,為了滿足特性二,需要分以下幾種情況討論:
① 如果 max_size<min_size,顯然需要把 data 插入到 maxheap 中;
② 如果 max_size>min_size,顯然需要把 data 插入到 minheap 中;
③ 如果 max_size==min_size,可以把 data 插入到任意乙個堆中。
3)如果 data>maxheap 的堆頂元素,此時為了滿足特性一,只能把 data 插入到 minheap 中。為了滿足特性二,需要分以下幾種情況討論:
① 如果 max_size≥min_size,那麼把 data 插入到 minheap 中;
② 如果 max_size<min_size,那麼需要把 minheap 堆頂元素移到 maxheap 中,然後把 data 插入到 minheap 中。
通過上述方法可以把 5 億個數構建成兩個堆,兩個堆頂元素的平均值就是中位數。
由於需要把所有的資料都載入到記憶體中,當資料量很大時,因為無法把資料一次性載入到記憶體中,因此這種方法比較適用於資料量小的情況。對本題而言,5 億個數字,每個數字在記憶體中佔 4b,5 億個數字需要的記憶體空間為 2gb 記憶體。當可用的記憶體不足 2gb 時,顯然不能使用這種方法,下面介紹另外一種方法。
方法二:分治法
分治法的核心思想是把乙個大的問題逐漸轉換為規模較小的問題來求解。對於本題而言,順序讀取這 5 億個數字;
1)對於讀取到的數字 num,如果它對應的二進位制中最高位為 1,則把這個數字寫入到 f1 中,如果最高位是 0,則寫入到 f0 中。通過這一步就可以把這 5 億個數字劃分成兩部分,而且 f0 中的數字都大於 f1 中的數字(因為最高位是符號位)。
2)通過上面的劃分很容易知道中位數是在 f0 中還是在 f1 中。假設 f1 中有 1 億個數,那麼中位數一定在檔案 f0 中,且它的值為 f0 檔案中的數字排序的第 1.5 億個數與它後面乙個數的平均值。
3)對於 f0,可以用次高位的二進位制的值繼續把這個檔案一分為二,使用同樣的思路可以確定中位數是哪個檔案中的第幾個數。直到劃分後的檔案可以被載入到記憶體時,把資料載入到記憶體中以後排序,從而找出中位數。
需要注意的是,這裡有一種特殊情況需要考慮,當資料總數為偶數時,如果把檔案一分為二後發現兩個檔案中的資料有相同的個數,那麼中位數就是 f0 檔案中的最大值與 f1 檔案中的最小值的平均值。如果要求乙個檔案中所有資料的最大值或最小值,可以使用前面介紹的分治法進行求解。
10 億個數取中位數
10 億個數取中位數 1 取 16 1024 個數,生成乙個 treemap 取得最大值 max 和最小值 min。2 構造乙個1024個元素的計數陣列 t i 對最初的 1024 個數按區間計數 對 min 和 max 進行 1024 個等分,各等分值為 n i 當資料 n a 3 將大於 n 5...
100億個整數,找出中位數
100億個整數,記憶體足夠,如何找到中位數?記憶體不足,如何找到中位數?1 當記憶體足夠時 採用快排,找到第n大的數。隨機選取乙個數,將比它小的元素放在它左邊,比它大的元素放在右邊 如果它恰好在中位數的位置,那麼它就是中位數,直接返回 如果小於它的數超過一半,那麼中位數一定在左半邊,遞迴到左邊處理 ...
求100億個數的中位數
給定100億個無符號的亂序的整數序列,如何求出這100億個數的中位數 中位數指的是排序後最中間那個數 乙個無符號整數的大小為4b,則100億個數的大小為40gb,如果記憶體夠大的話可以對這100億個數載入到記憶體中,然後使用堆排序或者快速排序進行排序,取出中位數即可。使用快排時,每次劃分之後只需要比...