RT,在兩個有序數組中找中位數或者第K大的元素.
假設兩個數組爲A, B長度分別爲m,n.分別是遞增順序。
可以採用的算法有很多:
首先想到的是類似MergeSort的方式,合併的同時找第K大元素,這個基本沒難度,複雜度O(m + n)。
不過此算法並不是最優,還有Log級別複雜度的算法,此算法其實很簡單,遠沒有很多網站的代碼那麼玄乎,以下一一道來:
首先明白幾個前提:
1.如果是求中位數,(m + n)是奇數還是偶數對結果是很有影響的,具體的如果(m + n)是奇數,中位數唯一,如果是偶數就有兩個中位數,怎麼取捨就看要求了。
2.如果找到的第k大數(中位數類似)是 X ,如果X排在A中的 第Ax位置,X排序在B中的Bx位置,那麼(Ax + Bx - 1) == k 是恆成立的。
明白了2中的前提後,我們就可以得到一個算法,在A數組中枚舉X,加入在A中是第Ax個,那麼可以反推B中第 (k + 1 - Ax)個以及相鄰元素和X的大小關係就可以得到一個Log級別複雜度的算法:
簡單點我們可以這麼想:
1)先假設第k大數在A中,我們首先從A中第(m/(m + n)) * (k - 1)個元素開始檢查其是否是第k個元素,假設其值爲A1,然後看B中第(k + 1 - (m/(m + n)) * (k - 1)個元素(B1)和A1是否相等,或者 大於B中第(k + 1 - (m/(m + n)) * (k - 1))個元素,小於B中第(k + 1 - (m/(m + n)) * (k - 1))+ 1個元素。滿足及可以知道A1即爲所求。如果兩個條件都不滿足,請看2.
2)如果兩個條件都不滿足,那麼需要判斷第k個元素是處於 A1的左邊還是右邊,這個就是典型的分治思想。具體的來說:
if A1 > B1 那麼k可以排除肯定不在A[0, (m/(m + n)) * (k - 1)]以及B[(k + 1 - (m/(m + n)) * (k - 1))+ 1, n]中
if A1< B1 那麼k可以排除肯定不在A[ (m/(m + n)) * (k - 1), m]以及B[0, (k + 1 - (m/(m + n)) * (k - 1))]中.
注意下臨界條件(corner condition may not stastify, but the method is right)
第K個元素有可能在B中,同理可以假設在B中,然後再搜索一遍就可以查到。複雜度 log(m)+ log(n)
當然也可以兩個數組一起找,總體代碼如下:
1 int kthsmallest(int *a,int m,int *b,int n,int k) { 2 if (m == 0) { 3 return b[k - 1]; 4 } 5 if (n == 0) { 6 return a[k - 1]; 7 } 8 if (k == 1) { 9 return (a[0] < b[0])?a[0]:b[0]; 10 } 11 if (k == m + n) { 12 return (a[m - 1] > b[n - 1])?a[m - 1]:b[n - 1]; 13 } 14 int i = ((double) m) / (m + n) * (k - 1); 15 int j = k - 1 - i; 16 if (j >= n) { 17 j = n - 1; 18 i = k - n; 19 } 20 if (((i == 0) || (a[i - 1] <= b[j])) && (b[j] <= a[i])) { 21 return b[j]; 22 } 23 if (((j == 0) || (b[j - 1] <= a[i])) && (a[i] <= b[j])) { 24 return a[i]; 25 } 26 if (a[i] <= b[j]) { 27 return kthsmallest(a + i + 1, m - i - 1, b, j, k - i - 1); 28 } else { 29 return kthsmallest(a, i, b + j + 1, n - j - 1, k - j - 1); 30 } 31 32 }