1. 程式人生 > >求多個有序陣列的中位數

求多個有序陣列的中位數

題目:求解多個有序陣列的中位數

題目的意思是如果多個有序陣列能在一起排序,則取位置為中間的數字,如果有奇數個數字則中位數只有一個;若為偶數個則有兩個,一般取第一個,也稱下中位。但不能把數組合在一起做插入或快速排序,因為資料可能是海量的。

該題目可能有很多種實現方法,而我們給出一種僅依賴中位數性質的演算法。如果存在一個已經排好序的大陣列(有序數列),則會發現幾個性質:

  1. 對稱性:中位數前面的數字與後面的數字一樣多(在偶數元素情況下或相差1)
  2. 確定性:若某個數字前後的數字數量相同(或相差1)則為該數列的中位數
  3. 不變性:刪除前N個元素與後N個元素後,中位數不變
  4. 關聯性:從大陣列中按順序任意取走n-1組元素,與剩下的元素共構成n個子列,每個子列存在中位數:m1
    ,m1...mn,min,max分別為其中最小者與最大者,則原數列的中位數m滿足:min<=m<=max
    該性質可通過反證法獲得
根據上述性質構造下面基於3分查詢的演算法:
  1. 輸入多個有序陣列查詢其中位數m
  2. 計算個數組的中位數,並計算其最小者min與最大者max,則m必滿足min<=m<=max
  3. 計算min之前所有的數字lTripCount(在所有陣列)及max之後所有的數字rTripCount,如果二者相同,則min與max都為中位數,遞迴結束
  4. 計算所有陣列中屬於區間[min,max]的中位數(小於min或大於max的全被捨棄)m1,如果m1滿足lTripCount==rTripCount,則為中位數,遞迴結束
  5. 如果lTripCount<rTripCount,則中位數m一定屬於區間[min,m1]
  6. 如果lTripCount>rTripCount,則中位數m一定屬於區間(m1,max]
  7. 遞迴上述過程
  8. 若某個遞迴後發現各陣列剩餘的資料小於某個閥值,則考慮一起計算,從而終止遞迴
該演算法的主要思想是利用子陣列的中位數逼近全陣列的中位數,具體過程如下:  要排序的全陣列:
其中10便是要求解的中位數,陣列A被劃分為下面三個子陣列:
計算步驟:
  1. 分別計算A1,A2,A3的中位數,分別為7,9,11
  2. 根據關聯性,則要尋找的中位數(10)必定滿足7<=m<=11
  3. 從A1,A2,A3中去除小於7,大於11的數,此時各子陣列的資料情況如下:

  4. 繼續計算各子陣列的中位數,可的A1:10,A2:9,A3:11,因為此時各陣列只剩一個元素,故根據假設可在一起計算其中位數10
  5. 以10為中心計算lTripCount、rTripCount,經過計算二者相同,根據確定性可斷定10為最終的中位數;如果lTreipCount與rTripCount不同,則可推導中位數會落在哪個區間(參考上面的演算法描述),從而遞迴上面的過程
因為每次都根據最小中位數與最大中位數過濾,故每次計算可大約砍掉2/3的資料,演算法大約以3n的速度收斂。 上述演算法可能會產生一個無法收斂的特殊場景:如果某個計算過程出現最小中位數為當前有效資料的最小值,而最大中位數為最大值,則無法過濾掉更多的資料,解決該問題的辦法是根據不變性,同時刪除最小中位數與最大中位數。